Exemplo n.º 1
0
    def open(self, user, path):
        if self.request.headers['Origin'] != 'http://%s' % (
                self.request.headers['Host']):
            self.log.warning(
                'Unauthorized connection attempt: from : %s to: %s' %
                (self.request.headers['Origin'], self.request.headers['Host']))
            self.close()
            return
        self.socket = utils.Socket(self.ws_connection.stream.socket)
        self.set_nodelay(True)
        self.log.info('Websocket opened %r' % self.socket)
        self.path = path
        self.user = user.decode('utf-8') if user else None
        self.caller = self.callee = None
        if self.socket.local:
            self.caller = utils.User(uid=self.socket.uid)
        else:
            # We don't know uid is on the other machine
            pass

        if self.user:
            try:
                self.callee = utils.User(name=self.user)
            except LookupError:
                print('User %s not found' % self.user)
                self.callee = None

        # If no user where given and we are local, keep the same user
        # as the one who opened the socket
        # ie: the one openning a terminal in borwser
        if not self.callee and not self.user and self.socket.local:
            self.callee = self.caller
        self.write_message(motd(self.socket, self.caller, self.callee))
        self.pty()
Exemplo n.º 2
0
    def determine_user(self):
        if not tornado.options.options.unsecure:
            # Secure mode we must have already a callee
            assert self.callee is not None
            return

        # If we should login, login
        if tornado.options.options.login:
            user = ''
            while user == '':
                try:
                    user = input('login: '******'t switch to user %s" % user, exc_info=True)
                self.callee = utils.User(name='nobody')
            return

        # if login is not required, we will use the same user as
        # butterfly is executed
        self.callee = self.callee or utils.User()
Exemplo n.º 3
0
    def shell(self):
        if self.callee is None:
            user = input('login: '******'nobody')

        try:
            os.chdir(self.path or self.callee.dir)
        except:
            pass

        env = os.environ
        env.update(self.socket.env)
        env["TERM"] = "xterm-256color"
        env["COLORTERM"] = "butterfly"
        env["HOME"] = self.callee.dir
        env["LOCATION"] = "http%s://%s:%d/" % (
            "s" if tornado.options.options.secure else "",
            tornado.options.options.host, tornado.options.options.port)
        env["PATH"] = '%s:%s' % (os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..',
                         'bin')), env.get("PATH"))

        if self.socket.local:
            # All users are the same -> launch shell
            if self.caller == self.callee and server == self.callee:
                args = [tornado.options.options.shell or self.callee.shell]
                args.append('-i')
                os.execvpe(args[0], args, env)
                # This process has been replaced
                return

            if server.root:
                if self.callee != self.caller:
                    # Force password prompt by dropping rights
                    # to the daemon user
                    os.setuid(daemon.uid)
        else:
            # We are not local so we should always get a password prompt
            if server.root:
                if self.callee == daemon:
                    # No logging from daemon
                    sys.exit(1)
                os.setuid(daemon.uid)

        if os.path.exists('/usr/bin/su'):
            args = ['/usr/bin/su']
        else:
            args = ['/bin/su']

        if sys.platform == 'linux':
            args.append('-p')
            if tornado.options.options.shell:
                args.append('-s')
                args.append(tornado.options.options.shell)
        args.append(self.callee.name)
        os.execvpe(args[0], args, env)
Exemplo n.º 4
0
    def create_terminal(self):
        socket = utils.Socket(self.ws_connection.stream.socket)
        user = self.request.query_arguments.get('user',
                                                [b''])[0].decode('utf-8')
        path = self.request.query_arguments.get('path',
                                                [b''])[0].decode('utf-8')
        secure_user = None

        if tornado.options.options.username is not None:
            user = utils.User(name=user)
        elif not tornado.options.options.unsecure:
            user = utils.parse_cert(
                self.ws_connection.stream.socket.getpeercert())

            assert user, 'No user in certificate'
            try:
                user = utils.User(name=user)
            except LookupError:
                raise Exception('Invalid user in certificate')

            # Certificate authed user
            secure_user = user

        elif socket.local and socket.user == utils.User() and not user:
            # Local to local returning browser user
            secure_user = socket.user
        elif user:
            try:
                user = utils.User(name=user)
            except LookupError:
                raise Exception('Invalid user')

        if secure_user:
            user = secure_user
            if self.session in self.sessions and self.session in (
                    self.sessions_secure_users):
                if user.name != self.sessions_secure_users[self.session]:
                    # Restrict to authorized users
                    raise tornado.web.HTTPError(403)
            else:
                self.sessions_secure_users[self.session] = user.name

        self.sessions[self.session].append(self)

        terminal = Terminal.sessions.get(self.session)
        # Handling terminal session
        if terminal:
            TermWebSocket.last.write_message(terminal.history)
            # And returning, we don't want another terminal
            return

        # New session, opening terminal
        terminal = Terminal(user, path, self.session, socket,
                            self.request.full_url().replace('/ctl/', '/'),
                            self.render_string, TermWebSocket.broadcast)

        terminal.pty()
        self.log.info('Openning session %s for secure user %r' %
                      (self.session, user))
Exemplo n.º 5
0
    def open(self, user, path):
        self.fd = None
        self.closed = False
        if self.request.headers['Origin'] not in (
                'http://%s' % self.request.headers['Host'],
                'https://%s' % self.request.headers['Host']):
            self.log.warning(
                'Unauthorized connection attempt: from : %s to: %s' %
                (self.request.headers['Origin'], self.request.headers['Host']))
            self.close()
            return

        self.socket = utils.Socket(self.ws_connection.stream.socket)
        self.set_nodelay(True)
        self.log.info('Websocket opened %r' % self.socket)
        self.path = path
        self.user = user if user else None
        self.caller = self.callee = None

        # If local we have the user connecting
        if self.socket.local and self.socket.user is not None:
            self.caller = self.socket.user

        if tornado.options.options.unsecure:
            if self.user:
                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
            # as the one who opened the socket
            # ie: the one openning a terminal in borwser
            if not self.callee and not self.user and self.socket.local:
                self.callee = self.caller
        else:
            user = utils.parse_cert(self.stream.socket.getpeercert())
            assert user, 'No user in certificate'
            self.user = user
            try:
                self.callee = utils.User(name=self.user)
            except LookupError:
                raise Exception('Invalid user in certificate')

        TermWebSocket.terminals.add(self)

        if tornado.options.options.motd != '':
            motd = (self.render_string(
                tornado.options.options.motd,
                butterfly=self,
                version=__version__,
                opts=tornado.options.options,
                colors=utils.ansi_colors).decode('utf-8').replace(
                    '\r', '').replace('\n', '\r\n'))
            self.write_message(motd)

        self.pty()
Exemplo n.º 6
0
    def open(self, user, path):
        if self.request.headers['Origin'] != 'http%s://%s' % (
                "s" if not tornado.options.options.unsecure else "",
                self.request.headers['Host']):
            self.log.warning(
                'Unauthorized connection attempt: from : %s to: %s' %
                (self.request.headers['Origin'], self.request.headers['Host']))
            self.close()
            return

        self.socket = utils.Socket(self.ws_connection.stream.socket)
        self.set_nodelay(True)
        self.log.info('Websocket opened %r' % self.socket)
        self.path = path
        self.user = user.decode('utf-8') if user else None
        self.caller = self.callee = None

        # If local we have the user connecting
        if self.socket.local and self.socket.user is not None:
            self.caller = self.socket.user

        if tornado.options.options.unsecure:
            if self.user:
                try:
                    self.callee = utils.User(name=self.user)
                except LookupError:
                    self.callee = None

            # If no user where given and we are local, keep the same user
            # as the one who opened the socket
            # ie: the one openning a terminal in borwser
            if not self.callee and not self.user and self.socket.local:
                self.callee = self.caller
        else:
            user = utils.parse_cert(self.request.get_ssl_certificate())
            assert user, 'No user in certificate'
            self.user = user
            try:
                self.callee = utils.User(name=self.user)
            except LookupError:
                raise Exception('Invalid user in certificate')

        self.write_message(motd(self.socket))
        self.pty()
Exemplo n.º 7
0
    def determine_user(self):
        if self.callee is None and (tornado.options.options.unsecure
                                    and tornado.options.options.login):
            # If callee is now known and we have unsecure connection
            user = ''
            while user == '':
                try:
                    user = input('login: '******'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):
            # if login is not required, we will use the same user as
            # butterfly is executed
            self.callee = utils.User()

        assert self.callee is not None
Exemplo n.º 8
0
    def __init__(self, user, path, session, socket, host, render_string, send):
        self.host = host
        self.session = session
        self.send = send
        self.fd = None
        self.closed = False
        self.socket = socket
        log.info('Terminal opening with session: %s and socket %r' % (
            self.session, self.socket))
        self.path = path
        self.user = user if user else None
        self.caller = self.callee = None

        # If local we have the user connecting
        if self.socket.local and self.socket.user is not None:
            self.caller = self.socket.user

        if tornado.options.options.unsecure:
            if self.user:
                try:
                    self.callee = utils.User(name=self.user)
                except LookupError:
                    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 as the one who opened the socket ie: the one
            # openning a terminal in browser
            if not self.callee and not self.user and self.socket.local:
                self.user = self.callee = self.caller
        else:
            # Authed user
            self.callee = self.user

        if tornado.options.options.motd != '':
            motd = (render_string(
                tornado.options.options.motd,
                butterfly=self,
                version=__version__,
                opts=tornado.options.options,
                colors=utils.ansi_colors)
                    .decode('utf-8')
                    .replace('\r', '')
                    .replace('\n', '\r\n'))
            self.send('S' + motd)

        log.info('Forking pty for user %r' % self.user)
Exemplo n.º 9
0
    server_cert.sign(ca_pk, 'sha512')

    write(cert % host, crypto.dump_certificate(crypto.FILETYPE_PEM,
                                               server_cert))
    write(cert_key % host,
          crypto.dump_privatekey(crypto.FILETYPE_PEM, server_pk))
    os.chmod(cert_key % host, stat.S_IRUSR | stat.S_IWUSR)  # 0o600 perms

    print('\nNow you can run --generate-user-pkcs=user '
          'to generate user certificate.')
    sys.exit(0)

if (options.generate_current_user_pkcs or options.generate_user_pkcs):
    from butterfly import utils
    try:
        current_user = utils.User()
    except Exception:
        current_user = None

    from OpenSSL import crypto
    if not all(map(os.path.exists, [ca, ca_key])):
        print('Please generate certificates using --generate-certs before')
        sys.exit(1)

    if options.generate_current_user_pkcs:
        user = current_user.name
    else:
        user = options.generate_user_pkcs

    if user != current_user.name and current_user.uid != 0:
        print('Cannot create certificate for another user with '
Exemplo n.º 10
0
import struct
import sys
from logging import getLogger

import tornado.ioloop
import tornado.options
import tornado.process
import tornado.web
import tornado.websocket

import termios
from butterfly import __version__, utils

log = getLogger('butterfly')
ioloop = tornado.ioloop.IOLoop.instance()
server = utils.User()
daemon = utils.User(name='daemon')

# Python 2 backward compatibility
try:
    input = raw_input
except NameError:
    pass


class Terminal(object):
    sessions = {}

    def __init__(self, user, path, session, socket, uri, render_string,
                 broadcast):
        self.sessions[session] = self
Exemplo n.º 11
0
    def shell(self):
        if not tornado.options.options.prompt_login:
            self.callee = utils.User(self.caller)
        if self.callee is None:
            user = input('login: '******'nobody')

        try:
            os.chdir(self.path or tornado.options.options.wd or self.callee.dir)
        except:
            pass

        env = os.environ
        env.update(self.socket.env)
        env["TERM"] = "xterm-256color"
        env["COLORTERM"] = "butterfly"
        env["HOME"] = self.callee.dir
        env["LOCATION"] = "http%s://%s:%d/" % (
            "s" if not tornado.options.options.unsecure else "",
            tornado.options.options.host, tornado.options.options.port)
        env["PATH"] = '%s:%s' % (os.path.abspath(os.path.join(
            os.path.dirname(__file__), '..', 'bin')), env.get("PATH"))

        env.pop("VIRTUAL_ENV", None)    # If the server is running from virtualenv
        env.pop("PS1", None)            # then remove the prefix (virtenv) and show the regular one [user@comp ~]

        if tornado.options.options.load_script:
            args = tornado.options.options.load_script.split(" ")
        elif tornado.options.options.shell:
            args = [tornado.options.options.shell]
        else:
            args = [self.callee.shell, "-i"]

        if self.socket.local or not tornado.options.options.prompt_login:
            # All users are the same -> launch shell
            if (self.caller == self.callee and server == self.callee) or not tornado.options.options.prompt_login:
                os.execvpe(args[0], args, env)
                # This process has been replaced
                return

            if server.root:
                if self.callee != self.caller:
                    # Force password prompt by dropping rights
                    # to the daemon user
                    os.setuid(daemon.uid)
        else:
            # We are not local so we should always get a password prompt
            if server.root:
                if self.callee == daemon:
                    # No logging from daemon
                    sys.exit(1)
                os.setuid(daemon.uid)

        if os.path.exists('/usr/bin/su'):
            args = ['/usr/bin/su']
        else:
            args = ['/bin/su']

        if sys.platform == 'linux':
            args.append('-p')
            if tornado.options.options.load_script:
                args.append('-c')
                args.append(tornado.options.options.load_script)
            elif tornado.options.options.shell:
                args.append('-s')
                args.append(tornado.options.options.shell)
        args.append(self.callee.name)
        os.execvpe(args[0], args, env)
Exemplo n.º 12
0
    def shell(self):
        if self.callee is None and (tornado.options.options.unsecure
                                    and tornado.options.options.login):
            # If callee is now known and we have unsecure connection
            user = input('login: '******'nobody')
        elif (tornado.options.options.unsecure
              and not tornado.options.options.login):
            # if login is not required, we will use the same user as
            # butterfly is executed
            self.callee = utils.User()

        assert self.callee is not None

        try:
            os.chdir(self.path or self.callee.dir)
        except:
            pass

        env = os.environ
        # If local and local user is the same as login user
        # We set the env of the user from the browser
        # Usefull when running as root
        if self.caller == self.callee:
            env.update(self.socket.env)
        env["TERM"] = "xterm-256color"
        env["COLORTERM"] = "butterfly"
        env["HOME"] = self.callee.dir
        env["LOCATION"] = "http%s://%s:%d/" % (
            "s" if not tornado.options.options.unsecure else "",
            tornado.options.options.host, tornado.options.options.port)
        env["PATH"] = '%s:%s' % (os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..',
                         'bin')), env.get("PATH"))

        if not tornado.options.options.unsecure or (
                self.socket.local and self.caller == self.callee and server
                == self.callee) or not tornado.options.options.login:
            # User has been auth with ssl or is the same user as server
            # or login is explicitly turned off
            if (not tornado.options.options.unsecure
                    and tornado.options.options.login
                    and not (self.socket.local and self.caller == self.callee
                             and server == self.callee)):
                # User is authed by ssl, setting groups
                try:
                    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')
                    sys.exit(1)

            if tornado.options.options.cmd:
                args = tornado.options.options.cmd.split(' ')
            else:
                args = [tornado.options.options.shell or self.callee.shell]
                args.append('-i')

            os.execvpe(args[0], args, env)
            # This process has been replaced

        # Unsecure connection with su
        if server.root:
            if self.socket.local:
                if self.callee != self.caller:
                    # Force password prompt by dropping rights
                    # to the daemon user
                    os.setuid(daemon.uid)
            else:
                # We are not local so we should always get a password prompt
                if self.callee == daemon:
                    # No logging from daemon
                    sys.exit(1)
                os.setuid(daemon.uid)

        if os.path.exists('/usr/bin/su'):
            args = ['/usr/bin/su']
        else:
            args = ['/bin/su']

        if sys.platform == 'linux':
            args.append('-p')
            if tornado.options.options.shell:
                args.append('-s')
                args.append(tornado.options.options.shell)
        args.append(self.callee.name)
        os.execvpe(args[0], args, env)
    def open(self, user, path, session):
        self.session = session
        self.closed = False
        self.secure_user = None

        # Prevent cross domain
        if self.request.headers['Origin'] not in (
                'http://%s' % self.request.headers['Host'],
                'https://%s' % self.request.headers['Host']):
            self.log.warning(
                'Unauthorized connection attempt: from : %s to: %s' %
                (self.request.headers['Origin'], self.request.headers['Host']))
            self.close()
            return

        TermWebSocket.sockets.append(self)

        self.log.info('Websocket opened %r' % self)
        self.set_nodelay(True)

        socket = utils.Socket(self.ws_connection.stream.socket)
        opts = tornado.options.options

        if not opts.unsecure:
            user = utils.parse_cert(
                self.ws_connection.stream.socket.getpeercert())
            assert user, 'No user in certificate'
            try:
                user = utils.User(name=user)
            except LookupError:
                raise Exception('Invalid user in certificate')

            # Certificate authed user
            self.secure_user = user

        elif socket.local and socket.user == utils.User():
            # Local to local returning browser user
            self.secure_user = socket.user

        # Handling terminal session
        if session:
            if session in self.user_sessions:
                # Session already here, registering websocket
                self.user_sessions[session].append(self)
                self.write_message('S' + TermWebSocket.history[session])
                # And returning, we don't want another terminal
                return
            else:
                # New session, opening terminal
                self.user_sessions[session] = [self]
                TermWebSocket.history[session] = ''

        terminal = Terminal(user, path, session, socket,
                            self.request.headers['Host'], self.render_string,
                            self.write)

        terminal.pty()

        if session:
            if not self.secure_user:
                self.log.error(
                    'No terminal session without secure authenticated user'
                    'or local user.')
                self._terminal = terminal
                self.session = None
            else:
                self.log.info('Openning session %s for secure user %r' %
                              (session, self.secure_user))
                self.user_terminals[session] = terminal
        else:
            self._terminal = terminal
Exemplo n.º 14
0
    def create_terminal(self):
        socket = utils.Socket(self.ws_connection.stream.socket)
        user = self.request.query_arguments.get('user',
                                                [b''])[0].decode('utf-8')
        path = self.request.query_arguments.get('path',
                                                [b''])[0].decode('utf-8')
        secure_user = None
        self.CMD_SETUP['path'] = str(path)

        if not tornado.options.options.unsecure:
            user = utils.parse_cert(
                self.ws_connection.stream.socket.getpeercert())
            assert user, 'No user in certificate'
            try:
                user = utils.User(name=user)
            except LookupError:
                raise Exception('Invalid user in certificate')

            # Certificate authed user
            secure_user = user

        elif socket.local and socket.user == utils.User() and not user:
            # Local to local returning browser user
            secure_user = socket.user
        elif user:
            try:
                user = utils.User(name=user)
            except LookupError:
                raise Exception('Invalid user')

        if secure_user:
            user = secure_user
            if self.session in self.sessions and self.session in (
                    self.sessions_secure_users):
                if user.name != self.sessions_secure_users[self.session]:
                    # Restrict to authorized users
                    raise tornado.web.HTTPError(403)
            else:
                self.sessions_secure_users[self.session] = user.name

        self.sessions[self.session].append(self)

        terminal = Terminal.sessions.get(self.session)
        # Handling terminal session
        if terminal:
            TermWebSocket.last.write_message(terminal.history)
            # And returning, we don't want another terminal
            return

        self.CMD_SETUP['path'] = str(path)
        self.CMD_SETUP['uri'] = str(self.request.full_url().replace(
            '/ctl/', '/'))
        self.CMD_SETUP['user'] = str(user)
        self.CMD_SETUP['urlparse'] = urlparse(self.CMD_SETUP['uri'])[4]
        self.CMD_SETUP['parse_qs'] = parse_qs(self.CMD_SETUP['urlparse'])
        wrapper_mode_config = get_wrapper_mode_config()
        MISSING_PARAMS = []
        INVALID_PARAMS = []
        for rp in wrapper_mode_config['REQUIRED_PARAMS']:
            if not rp in self.CMD_SETUP['parse_qs'].keys():
                MISSING_PARAMS.append(rp)
            else:
                IS_VALID_OPTION = True
                if rp in wrapper_mode_config['PARAM_OPTIONS'].keys():
                    IS_VALID_OPTION = False
                    for possible_value in wrapper_mode_config['PARAM_OPTIONS'][
                            rp]:
                        if self.CMD_SETUP['parse_qs'][rp][0] == possible_value:
                            IS_VALID_OPTION = True
                if not IS_VALID_OPTION:
                    INVALID_PARAMS.append(rp)

        if len(MISSING_PARAMS) > 0:
            err = f'{len(MISSING_PARAMS)} Missing URL parameters: {", ".join(MISSING_PARAMS)}'
            print(f'ERROR: {err}')
            tornado.options.options.cmd = "echo -e '{}'".format(err)
        elif len(INVALID_PARAMS) > 0:
            err = f'{len(INVALID_PARAMS)} Invalid URL parameters: {", ".join(INVALID_PARAMS)}'
            print(f'ERROR: {err}')
            tornado.options.options.cmd = "echo -e '{}'".format(err)
        else:
            HOST_NAME = self.CMD_SETUP['parse_qs']['hostname'][0]
            SERVICE_DESCRIPTION = ''
            tornado.options.options.cmd = write_script_template(self)
            sys.stderr.write('CMD={}\n'.format(tornado.options.options.cmd))

        # New session, opening terminal
        terminal = Terminal(user, path, self.session, socket,
                            self.request.full_url().replace('/ctl/', '/'),
                            self.render_string, TermWebSocket.broadcast,
                            self.CMD_SETUP)

        terminal.pty()
        self.log.info('Opening session %s for secure user %r' %
                      (self.session, user))