Ejemplo n.º 1
0
 def test_05_checksecure_forbiddenchar(self):
     """ U05 | forbid character, should return 1 """
     args = self.args + ["--forbidden=['l']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ls"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 1)
Ejemplo n.º 2
0
 def test_28_checksecure_quoted_command(self):
     """ U28 | quoted command should be parsed """
     INPUT = "echo'/1.sh'"
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)
Ejemplo n.º 3
0
 def test_04_checksecure_forbiddenpipe(self):
     """ U04 | forbid pipe, should return 1 """
     args = self.args + ["--forbidden=['|']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ls | ls"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 1)
Ejemplo n.º 4
0
 def test_28_checksecure_quoted_command(self):
     """ U28 | quoted command should be parsed """
     INPUT = "echo'/1.sh'"
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)
Ejemplo n.º 5
0
 def test_27_checksecure_quoted_command(self):
     """ U27 | quoted command should be parsed """
     INPUT = '"bash" && echo 1'
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)
Ejemplo n.º 6
0
    def __getattr__(self, attr):
        """ This method actually takes care of all the called method that are
        not resolved (i.e not existing methods). It actually will simulate
        the existence of any method    entered in the 'allowed' variable list.

        e.g. You just have to add 'uname' in list of allowed commands in
        the 'allowed' variable, and lshell will react as if you had
        added a do_uname in the ShellCmd class!
        """

        # expand environment variables in command line
        self.g_cmd = os.path.expandvars(self.g_cmd)
        self.g_line = os.path.expandvars(self.g_line)
        self.g_arg = os.path.expandvars(self.g_arg)

        # in case the configuration file has been modified, reload it
        if self.conf['config_mtime'] != os.path.getmtime(
                self.conf['configfile']):
            from lshell.checkconfig import CheckConfig
            self.conf = CheckConfig(['--config', self.conf['configfile']],
                                    refresh=1).returnconf()
            self.conf['promptprint'] = utils.updateprompt(os.getcwd(),
                                                          self.conf)
            self.log = self.conf['logpath']

        if self.g_cmd in ['quit', 'exit', 'EOF']:
            self.log.error('Exited')
            if self.g_cmd == 'EOF':
                self.stdout.write('\n')
            if self.conf['disable_exit'] != 1:
                sys.exit(0)

        # check that commands/chars present in line are allowed/secure
        ret_check_secure, self.conf = sec.check_secure(
            self.g_line,
            self.conf,
            strict=self.conf['strict'])
        if ret_check_secure == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            return object.__getattribute__(self, attr)

        # check that path present in line are allowed/secure
        ret_check_path, self.conf = sec.check_path(self.g_line,
                                                   self.conf,
                                                   strict=self.conf['strict'])
        if ret_check_path == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            # in case request was sent by WinSCP, return error code has to be
            # sent via an specific echo command
            if self.conf['winscp'] and re.search('WinSCP: this is end-of-file',
                                                 self.g_line):
                utils.exec_cmd('echo "WinSCP: this is end-of-file: %s"'
                               % self.retcode)
            return object.__getattribute__(self, attr)
        if self.g_cmd in self.conf['allowed']:
            if self.conf['timer'] > 0:
                self.mytimer(0)
            self.g_arg = re.sub('^~$|^~/', '%s/' % self.conf['home_path'],
                                self.g_arg)
            self.g_arg = re.sub(' ~/', ' %s/' % self.conf['home_path'],
                                self.g_arg)
            # replace previous command exit code
            # in case multiple commands (using separators), only replace first
            # command. Regex replaces all occurrences of $?, before ;,&,|
            if re.search('[;&\|]', self.g_line):
                p = re.compile("(\s|^)(\$\?)([\s|$]?[;&|].*)")
            else:
                p = re.compile("(\s|^)(\$\?)(\s|$)")
            self.g_line = p.sub(r' %s \3' % self.retcode, self.g_line)

            if type(self.conf['aliases']) == dict:
                self.g_line = utils.get_aliases(self.g_line,
                                                self.conf['aliases'])

            self.log.info('CMD: "%s"' % self.g_line)

            if self.g_cmd == 'cd':
                # split cd <dir> and rest of command
                cmd_split = re.split(';|&&|&|\|\||\|', self.g_line, 1)
                # in case the are commands following cd, first change the
                # directory, then execute the command
                if len(cmd_split) == 2:
                    directory, command = cmd_split
                    # only keep cd's argument
                    directory = directory.split('cd', 1)[1].strip()
                    # change directory then, if success, execute the rest of
                    # the cmd line
                    self.retcode, self.conf = builtins.cd(directory,
                                                          self.conf)

                    if self.retcode == 0:
                        self.retcode = utils.exec_cmd(command)
                else:
                    # set directory to command line argument and change dir
                    directory = self.g_arg
                    self.retcode, self.conf = builtins.cd(directory,
                                                          self.conf)

            # built-in lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = builtins.lpath(self.conf)
            # built-in lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = builtins.lsudo(self.conf)
            # built-in history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = builtins.history(self.conf, self.log)
            # built-in export function
            elif self.g_cmd == 'export':
                self.retcode, var = builtins.export(self.g_line)
                if self.retcode == 1:
                    self.log.critical("** forbidden environment variable '%s'"
                                      % var)
            # case 'cd' is in an alias e.g. {'toto':'cd /var/tmp'}
            elif self.g_line[0:2] == 'cd':
                self.g_cmd = self.g_line.split()[0]
                directory = ' '.join(self.g_line.split()[1:])
                self.retcode, self.conf = builtins.cd(directory,
                                                      self.conf)

            else:
                self.retcode = utils.exec_cmd(self.g_line)

        elif self.g_cmd not in ['', '?', 'help', None]:
            self.log.warn('INFO: unknown syntax -> "%s"' % self.g_line)
            self.stderr.write('*** unknown syntax: %s\n' % self.g_cmd)
        self.g_cmd, self.g_arg, self.g_line = ['', '', '']
        if self.conf['timer'] > 0:
            self.mytimer(self.conf['timer'])
        return object.__getattribute__(self, attr)
Ejemplo n.º 7
0
    def __getattr__(self, attr):
        """ This method actually takes care of all the called method that are
        not resolved (i.e not existing methods). It actually will simulate
        the existence of any method    entered in the 'allowed' variable list.

        e.g. You just have to add 'uname' in list of allowed commands in
        the 'allowed' variable, and lshell will react as if you had
        added a do_uname in the ShellCmd class!
        """

        # expand environment variables in command line
        self.g_cmd = os.path.expandvars(self.g_cmd)
        self.g_line = os.path.expandvars(self.g_line)
        self.g_arg = os.path.expandvars(self.g_arg)

        # in case the configuration file has been modified, reload it
        if self.conf['config_mtime'] != os.path.getmtime(
                self.conf['configfile']):
            from lshell.checkconfig import CheckConfig
            self.conf = CheckConfig(['--config', self.conf['configfile']],
                                    refresh=1).returnconf()
            self.conf['promptprint'] = utils.updateprompt(
                os.getcwd(), self.conf)
            self.log = self.conf['logpath']

        if self.g_cmd in ['quit', 'exit', 'EOF']:
            self.log.error('Exited')
            if self.g_cmd == 'EOF':
                self.stdout.write('\n')
            if self.conf['disable_exit'] != 1:
                sys.exit(0)

        # check that commands/chars present in line are allowed/secure
        ret_check_secure, self.conf = sec.check_secure(
            self.g_line, self.conf, strict=self.conf['strict'])
        if ret_check_secure == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            return object.__getattribute__(self, attr)

        # check that path present in line are allowed/secure
        ret_check_path, self.conf = sec.check_path(self.g_line,
                                                   self.conf,
                                                   strict=self.conf['strict'])
        if ret_check_path == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            # in case request was sent by WinSCP, return error code has to be
            # sent via an specific echo command
            if self.conf['winscp'] and re.search('WinSCP: this is end-of-file',
                                                 self.g_line):
                utils.exec_cmd('echo "WinSCP: this is end-of-file: %s"' %
                               self.retcode)
            return object.__getattribute__(self, attr)
        if self.g_cmd in self.conf['allowed']:
            if self.conf['timer'] > 0:
                self.mytimer(0)
            self.g_arg = re.sub('^~$|^~/', '%s/' % self.conf['home_path'],
                                self.g_arg)
            self.g_arg = re.sub(' ~/', ' %s/' % self.conf['home_path'],
                                self.g_arg)
            # replace previous command exit code
            # in case multiple commands (using separators), only replace first
            # command. Regex replaces all occurrences of $?, before ;,&,|
            if re.search('[;&|]', self.g_line):
                p = re.compile("(\s|^)(\$\?)([\s|$]?[;&|].*)")
            else:
                p = re.compile("(\s|^)(\$\?)(\s|$)")
            self.g_line = p.sub(r' %s \3' % self.retcode, self.g_line)

            if type(self.conf['aliases']) == dict:
                self.g_line = utils.get_aliases(self.g_line,
                                                self.conf['aliases'])

            self.log.info('CMD: "%s"' % self.g_line)

            if self.g_cmd == 'cd':
                # split cd <dir> and rest of command
                cmd_split = re.split(';|&&|&|\|\||\|', self.g_line, 1)
                # in case the are commands following cd, first change the
                # directory, then execute the command
                if len(cmd_split) == 2:
                    directory, command = cmd_split
                    # only keep cd's argument
                    directory = directory.split('cd', 1)[1].strip()
                    # change directory then, if success, execute the rest of
                    # the cmd line
                    self.retcode, self.conf = builtins.cd(directory, self.conf)

                    if self.retcode == 0:
                        self.retcode = utils.exec_cmd(command)
                else:
                    # set directory to command line argument and change dir
                    directory = self.g_arg
                    self.retcode, self.conf = builtins.cd(directory, self.conf)

            # built-in lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = builtins.lpath(self.conf)
            # built-in lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = builtins.lsudo(self.conf)
            # built-in history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = builtins.history(self.conf, self.log)
            # built-in export function
            elif self.g_cmd == 'export':
                self.retcode, var = builtins.export(self.g_line)
                if self.retcode == 1:
                    self.log.critical(
                        "** forbidden environment variable '%s'" % var)
            # case 'cd' is in an alias e.g. {'toto':'cd /var/tmp'}
            elif self.g_line[0:2] == 'cd':
                self.g_cmd = self.g_line.split()[0]
                directory = ' '.join(self.g_line.split()[1:])
                self.retcode, self.conf = builtins.cd(directory, self.conf)

            else:
                self.retcode = utils.exec_cmd(self.g_line)

        elif self.g_cmd not in ['', '?', 'help', None]:
            self.log.warn('INFO: unknown syntax -> "%s"' % self.g_line)
            self.stderr.write('*** unknown syntax: %s\n' % self.g_cmd)
        self.g_cmd, self.g_arg, self.g_line = ['', '', '']
        if self.conf['timer'] > 0:
            self.mytimer(self.conf['timer'])
        return object.__getattribute__(self, attr)
Ejemplo n.º 8
0
 def test_06_checksecure_sudo_command(self):
     """ U06 | quoted text should not be forbidden """
     INPUT = "sudo ls"
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)
Ejemplo n.º 9
0
 def test_07_checksecure_notallowed_command(self):
     """ U07 | forbidden command, should return 1 """
     args = self.args + ["--allowed=['ls']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ll"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 1)
Ejemplo n.º 10
0
 def test_04_checksecure_forbiddenpipe(self):
     """ U04 | forbid pipe, should return 1 """
     args = self.args + ["--forbidden=['|']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ls | ls"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 1)
Ejemplo n.º 11
0
 def test_05_checksecure_forbiddenchar(self):
     """ U05 | forbid character, should return 1 """
     args = self.args + ["--forbidden=['l']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ls"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 1)
Ejemplo n.º 12
0
 def test_03_checksecure_doublepipe(self):
     """ U03 | double pipes should be allowed, even if pipe is forbidden """
     args = self.args + ["--forbidden=['|']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ls || ls"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 0)
Ejemplo n.º 13
0
 def test_02_checksecure_simplequote(self):
     """ U02 | quoted text should not be forbidden """
     INPUT = "ls -E '1|2' tmp/test"
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 0)
Ejemplo n.º 14
0
 def test_01_checksecure_doublequote(self):
     """ U01 | quoted text should not be forbidden """
     INPUT = 'ls -E "1|2" tmp/test'
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 0)
Ejemplo n.º 15
0
 def test_06_checksecure_sudo_command(self):
     """ U06 | quoted text should not be forbidden """
     INPUT = "sudo ls"
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)
Ejemplo n.º 16
0
 def test_01_checksecure_doublequote(self):
     """ U01 | quoted text should not be forbidden """
     INPUT = 'ls -E "1|2" tmp/test'
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 0)
Ejemplo n.º 17
0
 def test_07_checksecure_notallowed_command(self):
     """ U07 | forbidden command, should return 1 """
     args = self.args + ["--allowed=['ls']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ll"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 1)
Ejemplo n.º 18
0
 def test_02_checksecure_simplequote(self):
     """ U02 | quoted text should not be forbidden """
     INPUT = "ls -E '1|2' tmp/test"
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 0)
Ejemplo n.º 19
0
    def check_scp_sftp(self):
        """ This method checks if the user is trying to SCP a file onto the
        server. If this is the case, it checks if the user is allowed to use
        SCP or not, and    acts as requested. : )
        """
        if 'ssh' in self.conf:
            if 'SSH_CLIENT' in os.environ \
               and 'SSH_TTY' not in os.environ:

                # check if sftp is requested and allowed
                if 'sftp-server' in self.conf['ssh']:
                    if self.conf['sftp'] is 1:
                        self.log.error('SFTP connect')
                        utils.exec_cmd(self.conf['ssh'])
                        self.log.error('SFTP disconnect')
                        sys.exit(0)
                    else:
                        self.log.error('*** forbidden SFTP connection')
                        sys.exit(0)

                # initialize cli session
                from lshell.shellcmd import ShellCmd
                cli = ShellCmd(self.conf,
                               None, None, None, None,
                               self.conf['ssh'])
                ret_check_path, self.conf = sec.check_path(self.conf['ssh'],
                                                           self.conf,
                                                           ssh=1)
                if ret_check_path == 1:
                    self.ssh_warn('path over SSH', self.conf['ssh'])

                # check if scp is requested and allowed
                if self.conf['ssh'].startswith('scp '):
                    if self.conf['scp'] is 1 or 'scp' in self.conf['overssh']:
                        if ' -f ' in self.conf['ssh']:
                            # case scp download is allowed
                            if self.conf['scp_download']:
                                self.log.error('SCP: GET "%s"'
                                               % self.conf['ssh'])
                            # case scp download is forbidden
                            else:
                                self.log.error('SCP: download forbidden: "%s"'
                                               % self.conf['ssh'])
                                sys.exit(0)
                        elif ' -t ' in self.conf['ssh']:
                            # case scp upload is allowed
                            if self.conf['scp_upload']:
                                if 'scpforce' in self.conf:
                                    cmdsplit = self.conf['ssh'].split(' ')
                                    scppath = os.path.realpath(cmdsplit[-1])
                                    forcedpath = os.path.realpath(self.conf
                                                                  ['scpforce'])
                                    if scppath != forcedpath:
                                        self.log.error('SCP: forced SCP '
                                                       'directory: %s'
                                                       % scppath)
                                        cmdsplit.pop(-1)
                                        cmdsplit.append(forcedpath)
                                        self.conf['ssh'] = string.join(
                                            cmdsplit)
                                self.log.error('SCP: PUT "%s"'
                                               % self.conf['ssh'])
                            # case scp upload is forbidden
                            else:
                                self.log.error('SCP: upload forbidden: "%s"'
                                               % self.conf['ssh'])
                                sys.exit(0)
                        utils.exec_cmd(self.conf['ssh'])
                        self.log.error('SCP disconnect')
                        sys.exit(0)
                    else:
                        self.ssh_warn('SCP connection',
                                      self.conf['ssh'],
                                      'scp')

                # check if command is in allowed overssh commands
                elif self.conf['ssh']:
                    # replace aliases
                    self.conf['ssh'] = utils.get_aliases(self.conf['ssh'],
                                                         self.conf['aliases'])
                    # if command is not "secure", exit
                    ret_check_secure, self.conf = sec.check_secure(
                        self.conf['ssh'],
                        self.conf,
                        strict=1,
                        ssh=1)
                    if ret_check_secure:
                        self.ssh_warn('char/command over SSH',
                                      self.conf['ssh'])
                    # else
                    self.log.error('Over SSH: "%s"' % self.conf['ssh'])
                    # if command is "help"
                    if self.conf['ssh'] == "help":
                        cli.do_help(None)
                    else:
                        utils.exec_cmd(self.conf['ssh'])
                    self.log.error('Exited')
                    sys.exit(0)

                # else warn and log
                else:
                    self.ssh_warn('command over SSH', self.conf['ssh'])

            else:
                # case of shell escapes
                self.ssh_warn('shell escape', self.conf['ssh'])
Ejemplo n.º 20
0
 def test_03_checksecure_doublepipe(self):
     """ U03 | double pipes should be allowed, even if pipe is forbidden """
     args = self.args + ["--forbidden=['|']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "ls || ls"
     return self.assertEqual(sec.check_secure(INPUT, userconf)[0], 0)
Ejemplo n.º 21
0
    def check_scp_sftp(self):
        """ This method checks if the user is trying to SCP a file onto the
        server. If this is the case, it checks if the user is allowed to use
        SCP or not, and    acts as requested. : )
        """
        if 'ssh' in self.conf:
            if 'SSH_CLIENT' in os.environ \
               and 'SSH_TTY' not in os.environ:

                # check if sftp is requested and allowed
                if 'sftp-server' in self.conf['ssh']:
                    if self.conf['sftp'] is 1:
                        self.log.error('SFTP connect')
                        utils.exec_cmd(self.conf['ssh'])
                        self.log.error('SFTP disconnect')
                        sys.exit(0)
                    else:
                        self.log.error('*** forbidden SFTP connection')
                        sys.exit(0)

                # initialize cli session
                from lshell.shellcmd import ShellCmd
                cli = ShellCmd(self.conf, None, None, None, None,
                               self.conf['ssh'])
                ret_check_path, self.conf = sec.check_path(self.conf['ssh'],
                                                           self.conf,
                                                           ssh=1)
                if ret_check_path == 1:
                    self.ssh_warn('path over SSH', self.conf['ssh'])

                # check if scp is requested and allowed
                if self.conf['ssh'].startswith('scp '):
                    if self.conf['scp'] is 1 or 'scp' in self.conf['overssh']:
                        if ' -f ' in self.conf['ssh']:
                            # case scp download is allowed
                            if self.conf['scp_download']:
                                self.log.error('SCP: GET "%s"' %
                                               self.conf['ssh'])
                            # case scp download is forbidden
                            else:
                                self.log.error(
                                    'SCP: download forbidden: "%s"' %
                                    self.conf['ssh'])
                                sys.exit(0)
                        elif ' -t ' in self.conf['ssh']:
                            # case scp upload is allowed
                            if self.conf['scp_upload']:
                                if 'scpforce' in self.conf:
                                    cmdsplit = self.conf['ssh'].split(' ')
                                    scppath = os.path.realpath(cmdsplit[-1])
                                    forcedpath = os.path.realpath(
                                        self.conf['scpforce'])
                                    if scppath != forcedpath:
                                        self.log.error('SCP: forced SCP '
                                                       'directory: %s' %
                                                       scppath)
                                        cmdsplit.pop(-1)
                                        cmdsplit.append(forcedpath)
                                        self.conf['ssh'] = string.join(
                                            cmdsplit)
                                self.log.error('SCP: PUT "%s"' %
                                               self.conf['ssh'])
                            # case scp upload is forbidden
                            else:
                                self.log.error('SCP: upload forbidden: "%s"' %
                                               self.conf['ssh'])
                                sys.exit(0)
                        utils.exec_cmd(self.conf['ssh'])
                        self.log.error('SCP disconnect')
                        sys.exit(0)
                    else:
                        self.ssh_warn('SCP connection', self.conf['ssh'],
                                      'scp')

                # check if command is in allowed overssh commands
                elif self.conf['ssh']:
                    # replace aliases
                    self.conf['ssh'] = utils.get_aliases(
                        self.conf['ssh'], self.conf['aliases'])
                    # if command is not "secure", exit
                    ret_check_secure, self.conf = sec.check_secure(
                        self.conf['ssh'], self.conf, strict=1, ssh=1)
                    if ret_check_secure:
                        self.ssh_warn('char/command over SSH',
                                      self.conf['ssh'])
                    # else
                    self.log.error('Over SSH: "%s"' % self.conf['ssh'])
                    # if command is "help"
                    if self.conf['ssh'] == "help":
                        cli.do_help(None)
                    else:
                        utils.exec_cmd(self.conf['ssh'])
                    self.log.error('Exited')
                    sys.exit(0)

                # else warn and log
                else:
                    self.ssh_warn('command over SSH', self.conf['ssh'])

            else:
                # case of shell escapes
                self.ssh_warn('shell escape', self.conf['ssh'])
Ejemplo n.º 22
0
 def test_27_checksecure_quoted_command(self):
     """ U27 | quoted command should be parsed """
     INPUT = '"bash" && echo 1'
     return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)