Esempio n. 1
0
 def test_16_allowed_ld_preload_builtin(self):
     """ U16 | builtin commands should NOT be prepended with LD_PRELOAD """
     args = self.args + ["--allowed=['echo','export']"]
     userconf = CheckConfig(args).returnconf()
     # verify that export is not automatically added to the aliases (i.e.
     # prepended with LD_PRELOAD)
     return self.assertNotIn('export', userconf['aliases'])
Esempio n. 2
0
 def test_15_allowed_ld_preload_cmd(self):
     """ U15 | all allowed commands should be prepended with LD_PRELOAD """
     args = self.args + ["--allowed=['echo','export']"]
     userconf = CheckConfig(args).returnconf()
     # sort lists to compare
     return self.assertEqual(userconf['aliases']['echo'],
                             'LD_PRELOAD=%s echo' % userconf['path_noexec'])
Esempio n. 3
0
 def test_09_checkpath_notallowed_path_completion(self):
     """ U09 | forbidden command, should return 1 """
     args = self.args + ["--path=['/home', '/var']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "cd /tmp/"
     return self.assertEqual(
         sec.check_path(INPUT, userconf, completion=1)[0], 1)
Esempio n. 4
0
 def test_17_allowed_exec_cmd(self):
     """ U17 | allowed_shell_escape should NOT be prepended with LD_PRELOAD
         The command should not be added to the aliases variable
     """
     args = self.args + ["--allowed_shell_escape=['echo']"]
     userconf = CheckConfig(args).returnconf()
     # sort lists to compare
     return self.assertNotIn('echo', userconf['aliases'])
Esempio n. 5
0
 def test_22_prompt_short_0(self):
     """ U22 | short_prompt = 0 should show dir compared to home dir """
     expected = '%s:~/foo$ ' % getuser()
     args = self.args + ['--prompt_short=0']
     userconf = CheckConfig(args).returnconf()
     currentpath = "%s/foo" % userconf['home_path']
     prompt = updateprompt(currentpath, userconf)
     # sort lists to compare
     return self.assertEqual(prompt, expected)
Esempio n. 6
0
 def test_23_prompt_short_1(self):
     """ U23 | short_prompt = 1 should show only current dir """
     expected = '%s: foo$ ' % getuser()
     args = self.args + ['--prompt_short=1']
     userconf = CheckConfig(args).returnconf()
     currentpath = "%s/foo" % userconf['home_path']
     prompt = updateprompt(currentpath, userconf)
     # sort lists to compare
     return self.assertEqual(prompt, expected)
Esempio n. 7
0
 def test_12_overssh(self):
     """ U12 | command over ssh """
     args = self.args + ["--overssh=['exit']", '-c exit']
     os.environ['SSH_CLIENT'] = '8.8.8.8 36000 22'
     if 'SSH_TTY' in os.environ:
         os.environ.pop('SSH_TTY')
     with self.assertRaises(SystemExit) as cm:
         CheckConfig(args).returnconf()
     return self.assertEqual(cm.exception.code, 0)
Esempio n. 8
0
 def test_24_prompt_short_2(self):
     """ U24 | short_prompt = 2 should show full dir path """
     expected = '%s: %s$ ' % (getuser(), os.getcwd())
     args = self.args + ['--prompt_short=2']
     userconf = CheckConfig(args).returnconf()
     currentpath = "%s/foo" % userconf['home_path']
     prompt = updateprompt(currentpath, userconf)
     # sort lists to compare
     return self.assertEqual(prompt, expected)
Esempio n. 9
0
 def test_14_sudo_all_commands_expansion(self):
     """ U14 | sudo_commands set to 'all' is equal to allowed variable """
     args = self.args + ["--sudo_commands=all"]
     userconf = CheckConfig(args).returnconf()
     # exclude internal and sudo(8) commands
     exclude = builtins_list + ['sudo']
     allowed = [x for x in userconf['allowed'] if x not in exclude]
     # sort lists to compare
     userconf['sudo_commands'].sort()
     allowed.sort()
     return self.assertEqual(allowed, userconf['sudo_commands'])
Esempio n. 10
0
 def test_20_winscp_allowed_commands(self):
     """ U20 | when winscp is enabled, new allowed commands are automatically
         added (see man).
     """
     args = self.args + ["--allowed=[]", "--winscp=1"]
     userconf = CheckConfig(args).returnconf()
     # sort lists to compare, except 'export'
     exclude = list(set(builtins_list) - set(['export']))
     expected = exclude + [
         'scp', 'env', 'pwd', 'groups', 'unset', 'unalias'
     ]
     expected.sort()
     allowed = userconf['allowed']
     allowed.sort()
     return self.assertEqual(allowed, expected)
Esempio n. 11
0
 def test_11_checkconfig_configoverwrite(self):
     """ U12 | forbid ';', then check_secure should return 1 """
     args = ['--config=%s/etc/lshell.conf' % TOPDIR, '--strict=123']
     userconf = CheckConfig(args).returnconf()
     return self.assertEqual(userconf['strict'], 123)
Esempio n. 12
0
 def test_10_checkpath_dollarparenthesis(self):
     """ U10 | when $() is allowed, return 0 if path allowed """
     args = self.args + ["--forbidden=[';', '&', '|','`','>','<', '${']"]
     userconf = CheckConfig(args).returnconf()
     INPUT = "echo $(echo aze)"
     return self.assertEqual(sec.check_path(INPUT, userconf)[0], 0)
Esempio n. 13
0
class ShellCmd(cmd.Cmd, object):
    """ Main lshell CLI class
    """

    def __init__(self, userconf, args, stdin=None, stdout=None, stderr=None, g_cmd=None, g_line=None):
        if stdin is None:
            self.stdin = sys.stdin
        else:
            self.stdin = stdin
        if stdout is None:
            self.stdout = sys.stdout
        else:
            self.stdout = stdout
        if stderr is None:
            self.stderr = sys.stderr
        else:
            self.stderr = stderr

        self.args = args
        self.conf = userconf
        self.log = self.conf["logpath"]

        # Set timer
        if self.conf["timer"] > 0:
            self.mytimer(self.conf["timer"])
        self.identchars = self.identchars + "+./-"
        self.log.error("Logged in")
        cmd.Cmd.__init__(self)
        if "prompt" in self.conf:
            self.promptbase = self.conf["prompt"]
            self.promptbase = self.promptbase.replace("%u", getuser())
            self.promptbase = self.promptbase.replace("%h", os.uname()[1].split(".")[0])
        else:
            self.promptbase = getuser()

        self.prompt = "%s:~$ " % self.promptbase

        self.intro = self.conf["intro"]

        # initialize oldpwd variable to home directory
        self.oldpwd = self.conf["home_path"]

        # initialize cli variables
        self.g_cmd = g_cmd
        self.g_line = g_line

        # initialize return code
        self.retcode = None

    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 existance 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"]]).returnconf()
            self.prompt = "%s:~$ " % self.setprompt(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")
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf["strict"]) == 1:
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict=self.conf["strict"]) == 1:
            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 occureces 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 = get_aliases(self.g_line, self.conf["aliases"])

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

            if self.g_cmd == "cd":
                if re.search("[;&\|]", self.g_line):
                    # ignore internal cd function in case more than one command
                    self.retcode = exec_cmd(self.g_line)
                else:
                    # builtin cd function
                    self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == "lpath":
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == "lsudo":
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == "history":
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == "export":
                self.retcode = self.export()
            # 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]
                self.g_arg = " ".join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = 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)

    def setprompt(self, conf):
        """ set prompt used by the shell
        """
        if conf.has_key("prompt"):
            promptbase = conf["prompt"]
            promptbase = promptbase.replace("%u", getuser())
            promptbase = promptbase.replace("%h", os.uname()[1].split(".")[0])
        else:
            promptbase = getuser()

        return promptbase

    def lpath(self):
        """ lists allowed and forbidden path
        """
        if self.conf["path"][0]:
            sys.stdout.write("Allowed:\n")
            for path in self.conf["path"][0].split("|"):
                if path:
                    sys.stdout.write(" %s\n" % path[:-2])
        if self.conf["path"][1]:
            sys.stdout.write("Denied:\n")
            for path in self.conf["path"][1].split("|"):
                if path:
                    sys.stdout.write(" %s\n" % path[:-2])
        return 0

    def lsudo(self):
        """ lists allowed sudo commands
        """
        if self.conf.has_key("sudo_commands"):
            sys.stdout.write("Allowed sudo commands:\n")
            for command in self.conf["sudo_commands"]:
                sys.stdout.write(" - %s\n" % command)
        return 0

    def history(self):
        """ print the commands history
        """
        try:
            try:
                readline.write_history_file(self.conf["history_file"])
            except IOError:
                self.log.error("WARN: couldn't write history " "to file %s\n" % self.conf["history_file"])
                return 1
            f = open(self.conf["history_file"], "r")
            i = 1
            for item in f.readlines():
                sys.stdout.write("%d:  %s" % (i, item))
                i += 1
        except:
            self.log.critical("** Unable to read the history file.")
            return 1
        return 0

    def export(self):
        """ export environment variables """
        # if command contains at least 1 space
        if self.g_line.count(" "):
            env = self.g_line.split(" ", 1)[1]
            # if it conatins the equal sign, consider only the first one
            if env.count("="):
                var, value = env.split(" ")[0].split("=")[0:2]
                os.environ.update({var: value})
        return 0

    def cd(self):
        """ implementation of the "cd" command
        """
        if len(self.g_arg) >= 1:
            # add wildcard completion support to cd
            if self.g_arg.find("*"):
                # get all files and directories matching wildcard
                wildall = glob.glob(self.g_arg)
                wilddir = []
                # filter to only directories
                for item in wildall:
                    if os.path.isdir(item):
                        wilddir.append(item)
                # sort results
                wilddir.sort()
                # if any results are returned, pick first one
                if len(wilddir) >= 1:
                    self.g_arg = wilddir[0]
            # go previous directory
            if self.g_arg == "-":
                self.g_arg = self.oldpwd

            # store current directory in oldpwd variable
            self.oldpwd = os.getcwd()

            # change directory
            try:
                os.chdir(os.path.realpath(self.g_arg))
                self.updateprompt(os.getcwd())
            except OSError, (ErrorNumber, ErrorMessage):
                sys.stdout.write("lshell: %s: %s\n" % (self.g_arg, ErrorMessage))
                return ErrorNumber
        else:
Esempio n. 14
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)
Esempio n. 15
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)
Esempio n. 16
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)
Esempio n. 17
0
 def test_25_disable_ld_preload(self):
     """ U25 | empty path_noexec should disable LD_PRELOAD """
     args = self.args + ["--allowed=['echo','export']", "--path_noexec=''"]
     userconf = CheckConfig(args).returnconf()
     # verify that no alias was created containing LD_PRELOAD
     return self.assertNotIn('echo', userconf['aliases'])
Esempio n. 18
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 existance 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!
        """

        # 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']]).returnconf()
            self.prompt = '%s:~$ ' % self.setprompt(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')
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf['strict']) == 1: 
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict = self.conf['strict']) == 1:
            return object.__getattribute__(self, attr)
        if self.g_cmd in self.conf['allowed']:
            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
            self.g_line = re.sub('(\s|^)\$\?(\s|$)', ' %s '  %self.retcode,
                                                                   self.g_line)

            if type(self.conf['aliases']) == dict:
                self.g_line = get_aliases(self.g_line, self.conf['aliases'])
            self.log.info('CMD: "%s"' %self.g_line)
            if self.g_cmd == 'cd':
                self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == 'export':
                self.retcode = self.export()
            # 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]
                self.g_arg = ' '.join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = os.system('set -m; %s' % 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 = ['', '', ''] 
        return object.__getattribute__(self, attr)
Esempio n. 19
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 existance 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"]]).returnconf()
            self.prompt = "%s:~$ " % self.setprompt(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")
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf["strict"]) == 1:
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict=self.conf["strict"]) == 1:
            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 occureces 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 = get_aliases(self.g_line, self.conf["aliases"])

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

            if self.g_cmd == "cd":
                if re.search("[;&\|]", self.g_line):
                    # ignore internal cd function in case more than one command
                    self.retcode = exec_cmd(self.g_line)
                else:
                    # builtin cd function
                    self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == "lpath":
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == "lsudo":
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == "history":
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == "export":
                self.retcode = self.export()
            # 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]
                self.g_arg = " ".join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = 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)
Esempio n. 20
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 existance 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!
        """

        # 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']]).returnconf()
            self.prompt = '%s:~$ ' % self.setprompt(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')
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf['strict']) == 1:
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict=self.conf['strict']) == 1:
            return object.__getattribute__(self, attr)
        if self.g_cmd in self.conf['allowed']:
            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
            self.g_line = re.sub('(\s|^)\$\?(\s|$)', ' %s ' % self.retcode,
                                 self.g_line)

            if type(self.conf['aliases']) == dict:
                self.g_line = get_aliases(self.g_line, self.conf['aliases'])
            self.log.info('CMD: "%s"' % self.g_line)
            if self.g_cmd == 'cd':
                self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == 'export':
                self.retcode = self.export()
            # 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]
                self.g_arg = ' '.join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = os.system('set -m; %s' % 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 = ['', '', '']
        return object.__getattribute__(self, attr)
Esempio n. 21
0
class ShellCmd(cmd.Cmd, object):
    """ Main lshell CLI class
    """

    def __init__(self, userconf, args, stdin=None, stdout=None, stderr=None,
                 g_cmd=None, g_line=None):
        if stdin is None:
            self.stdin = sys.stdin
        else:
            self.stdin = stdin
        if stdout is None:
            self.stdout = sys.stdout
        else:
            self.stdout = stdout
        if stderr is None:
            self.stderr = sys.stderr
        else:
            self.stderr = stderr

        self.args = args
        self.conf = userconf
        self.log = self.conf['logpath']

        # Set timer
        if self.conf['timer'] > 0:
            self.mytimer(self.conf['timer'])
        self.identchars = self.identchars + '+./-'
        self.log.error('Logged in')
        cmd.Cmd.__init__(self)
        if 'prompt' in self.conf:
            self.promptbase = self.conf['prompt']
            self.promptbase = self.promptbase.replace('%u', getuser())
            self.promptbase = self.promptbase.replace(
                '%h',
                os.uname()[1].split('.')[0])
        else:
            self.promptbase = getuser()

        self.prompt = '%s:~$ ' % self.promptbase

        self.intro = self.conf['intro']

        # initialize oldpwd variable to home directory
        self.oldpwd = self.conf['home_path']

        # initialize cli variables
        self.g_cmd = g_cmd
        self.g_line = g_line

        # initialize return code
        self.retcode = 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 existance 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']]).returnconf()
            self.prompt = '%s:~$ ' % self.setprompt(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')
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf['strict']) == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict = self.conf['strict']) == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            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 occureces 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 = get_aliases(self.g_line, self.conf['aliases'])

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

            if self.g_cmd == 'cd':
              if re.search('[;&\|]', self.g_line):
                # ignore internal cd function in case more than one command
                self.retcode = exec_cmd(self.g_line)
              else:
                # builtin cd function
                self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == 'export':
                self.retcode = self.export()
            # 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]
                self.g_arg = ' '.join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = 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)

    def setprompt(self, conf):
        """ set prompt used by the shell
        """
        if conf.has_key('prompt'):
            promptbase = conf['prompt']
            promptbase = promptbase.replace('%u', getuser())
            promptbase = promptbase.replace('%h', os.uname()[1].split('.')[0])
        else:
            promptbase = getuser()

        return promptbase

    def lpath(self):
        """ lists allowed and forbidden path
        """
        if self.conf['path'][0]:
            sys.stdout.write("Allowed:\n")
            lpath_allowed = self.conf['path'][0].split('|')
            lpath_allowed.sort()
            for path in lpath_allowed:
                if path:
                    sys.stdout.write(" %s\n" % path[:-2])
        if self.conf['path'][1]:
            sys.stdout.write("Denied:\n")
            lpath_denied = self.conf['path'][1].split('|')
            lpath_denied.sort()
            for path in lpath_denied:
                if path:
                    sys.stdout.write(" %s\n" % path[:-2])
        return 0

    def lsudo(self):
        """ lists allowed sudo commands
        """
        if self.conf.has_key('sudo_commands') and len(self.conf['sudo_commands']) > 0:
            sys.stdout.write("Allowed sudo commands:\n")
            for command in self.conf['sudo_commands']:
                sys.stdout.write(" - %s\n" % command)
            return 0
        else:
            sys.stdout.write("No sudo commands allowed\n")
            return 1

    def history(self):
        """ print the commands history
        """
        try:
            try:
                readline.write_history_file(self.conf['history_file'])
            except IOError:
                self.log.error('WARN: couldn\'t write history ' \
                                   'to file %s\n' % self.conf['history_file'])
                return 1
            f = open(self.conf['history_file'], 'r')
            i = 1
            for item in f.readlines():
                sys.stdout.write("%d:  %s" % (i, item))
                i += 1
        except:
            self.log.critical('** Unable to read the history file.')
            return 1
        return 0

    def export(self):
        """ export environment variables """
        # if command contains at least 1 space
        if self.g_line.count(' '):
            env = self.g_line.split(" ", 1)[1]
            # if it conatins the equal sign, consider only the first one
            if env.count('='):
                var, value = env.split(' ')[0].split('=')[0:2]
                os.environ.update({var: value})
        return 0

    def cd(self):
        """ implementation of the "cd" command
        """
        if len(self.g_arg) >= 1:
            # add wildcard completion support to cd
            if self.g_arg.find('*'):
                # get all files and directories matching wildcard
                wildall = glob.glob(self.g_arg)
                wilddir = []
                # filter to only directories
                for item in wildall:
                    if os.path.isdir(item):
                        wilddir.append(item)
                # sort results
                wilddir.sort()
                # if any results are returned, pick first one
                if len(wilddir) >= 1:
                    self.g_arg = wilddir[0]
            # go previous directory
            if self.g_arg == '-':
                self.g_arg = self.oldpwd

            # store current directory in oldpwd variable
            self.oldpwd = os.getcwd()

            # change directory
            try:
                os.chdir(os.path.realpath(self.g_arg))
                self.updateprompt(os.getcwd())
            except OSError, (ErrorNumber, ErrorMessage):
                sys.stdout.write("lshell: %s: %s\n" %(self.g_arg, ErrorMessage))
                return ErrorNumber
        else:
Esempio n. 22
0
 def test_21_winscp_allowed_semicolon(self):
     """ U21 | when winscp is enabled, use of semicolon is allowed """
     args = self.args + ["--forbidden=[';']", "--winscp=1"]
     userconf = CheckConfig(args).returnconf()
     # sort lists to compare
     return self.assertNotIn(';', userconf['forbidden'])
Esempio n. 23
0
class TestFunctions(unittest.TestCase):
    args = ['--config=%s/etc/lshell.conf' % TOPDIR, "--quiet=1"]
    userconf = CheckConfig(args).returnconf()
    shell = ShellCmd(userconf, args)

    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)

    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)

    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)

    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)

    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)

    def test_08_checkpath_notallowed_path(self):
        """ U08 | forbidden command, should return 1 """
        args = self.args + ["--path=['/home', '/var']"]
        userconf = CheckConfig(args).returnconf()
        INPUT = "cd /tmp"
        return self.assertEqual(sec.check_path(INPUT, userconf)[0], 1)

    def test_09_checkpath_notallowed_path_completion(self):
        """ U09 | forbidden command, should return 1 """
        args = self.args + ["--path=['/home', '/var']"]
        userconf = CheckConfig(args).returnconf()
        INPUT = "cd /tmp/"
        return self.assertEqual(
            sec.check_path(INPUT, userconf, completion=1)[0], 1)

    def test_10_checkpath_dollarparenthesis(self):
        """ U10 | when $() is allowed, return 0 if path allowed """
        args = self.args + ["--forbidden=[';', '&', '|','`','>','<', '${']"]
        userconf = CheckConfig(args).returnconf()
        INPUT = "echo $(echo aze)"
        return self.assertEqual(sec.check_path(INPUT, userconf)[0], 0)

    def test_11_checkconfig_configoverwrite(self):
        """ U12 | forbid ';', then check_secure should return 1 """
        args = ['--config=%s/etc/lshell.conf' % TOPDIR, '--strict=123']
        userconf = CheckConfig(args).returnconf()
        return self.assertEqual(userconf['strict'], 123)

    def test_12_overssh(self):
        """ U12 | command over ssh """
        args = self.args + ["--overssh=['exit']", '-c exit']
        os.environ['SSH_CLIENT'] = '8.8.8.8 36000 22'
        if 'SSH_TTY' in os.environ:
            os.environ.pop('SSH_TTY')
        with self.assertRaises(SystemExit) as cm:
            CheckConfig(args).returnconf()
        return self.assertEqual(cm.exception.code, 0)

    def test_13_multiple_aliases_with_separator(self):
        """ U13 | multiple aliases using &&, || and ; separators """
        # enable &, | and ; characters
        aliases = {'foo': 'foo -l', 'bar': 'open'}
        INPUT = "foo; fooo  ;bar&&foo  &&   foo | bar||bar   ||     foo"
        return self.assertEqual(
            get_aliases(INPUT, aliases), ' foo -l; fooo  ; open&& foo -l  '
            '&& foo -l | open|| open   || foo -l')

    def test_14_sudo_all_commands_expansion(self):
        """ U14 | sudo_commands set to 'all' is equal to allowed variable """
        args = self.args + ["--sudo_commands=all"]
        userconf = CheckConfig(args).returnconf()
        # exclude internal and sudo(8) commands
        exclude = builtins_list + ['sudo']
        allowed = [x for x in userconf['allowed'] if x not in exclude]
        # sort lists to compare
        userconf['sudo_commands'].sort()
        allowed.sort()
        return self.assertEqual(allowed, userconf['sudo_commands'])

    def test_15_allowed_ld_preload_cmd(self):
        """ U15 | all allowed commands should be prepended with LD_PRELOAD """
        args = self.args + ["--allowed=['echo','export']"]
        userconf = CheckConfig(args).returnconf()
        # sort lists to compare
        return self.assertEqual(userconf['aliases']['echo'],
                                'LD_PRELOAD=%s echo' % userconf['path_noexec'])

    def test_16_allowed_ld_preload_builtin(self):
        """ U16 | builtin commands should NOT be prepended with LD_PRELOAD """
        args = self.args + ["--allowed=['echo','export']"]
        userconf = CheckConfig(args).returnconf()
        # verify that export is not automatically added to the aliases (i.e.
        # prepended with LD_PRELOAD)
        return self.assertNotIn('export', userconf['aliases'])

    def test_17_allowed_exec_cmd(self):
        """ U17 | allowed_shell_escape should NOT be prepended with LD_PRELOAD
            The command should not be added to the aliases variable
        """
        args = self.args + ["--allowed_shell_escape=['echo']"]
        userconf = CheckConfig(args).returnconf()
        # sort lists to compare
        return self.assertNotIn('echo', userconf['aliases'])

    def test_18_forbidden_environment(self):
        """ U18 | unsafe environment are forbidden
        """
        INPUT = 'export LD_PRELOAD=/lib64/ld-2.21.so'
        args = INPUT
        retcode = builtins.export(args)[0]
        return self.assertEqual(retcode, 1)

    def test_19_allowed_environment(self):
        """ U19 | other environment are accepted
        """
        INPUT = 'export MY_PROJECT_VERSION=43'
        args = INPUT
        retcode = builtins.export(args)[0]
        return self.assertEqual(retcode, 0)

    def test_20_winscp_allowed_commands(self):
        """ U20 | when winscp is enabled, new allowed commands are automatically
            added (see man).
        """
        args = self.args + ["--allowed=[]", "--winscp=1"]
        userconf = CheckConfig(args).returnconf()
        # sort lists to compare, except 'export'
        exclude = list(set(builtins_list) - set(['export']))
        expected = exclude + [
            'scp', 'env', 'pwd', 'groups', 'unset', 'unalias'
        ]
        expected.sort()
        allowed = userconf['allowed']
        allowed.sort()
        return self.assertEqual(allowed, expected)

    def test_21_winscp_allowed_semicolon(self):
        """ U21 | when winscp is enabled, use of semicolon is allowed """
        args = self.args + ["--forbidden=[';']", "--winscp=1"]
        userconf = CheckConfig(args).returnconf()
        # sort lists to compare
        return self.assertNotIn(';', userconf['forbidden'])

    def test_22_prompt_short_0(self):
        """ U22 | short_prompt = 0 should show dir compared to home dir """
        expected = '%s:~/foo$ ' % getuser()
        args = self.args + ['--prompt_short=0']
        userconf = CheckConfig(args).returnconf()
        currentpath = "%s/foo" % userconf['home_path']
        prompt = updateprompt(currentpath, userconf)
        # sort lists to compare
        return self.assertEqual(prompt, expected)

    def test_23_prompt_short_1(self):
        """ U23 | short_prompt = 1 should show only current dir """
        expected = '%s: foo$ ' % getuser()
        args = self.args + ['--prompt_short=1']
        userconf = CheckConfig(args).returnconf()
        currentpath = "%s/foo" % userconf['home_path']
        prompt = updateprompt(currentpath, userconf)
        # sort lists to compare
        return self.assertEqual(prompt, expected)

    def test_24_prompt_short_2(self):
        """ U24 | short_prompt = 2 should show full dir path """
        expected = '%s: %s$ ' % (getuser(), os.getcwd())
        args = self.args + ['--prompt_short=2']
        userconf = CheckConfig(args).returnconf()
        currentpath = "%s/foo" % userconf['home_path']
        prompt = updateprompt(currentpath, userconf)
        # sort lists to compare
        return self.assertEqual(prompt, expected)

    def test_25_disable_ld_preload(self):
        """ U25 | empty path_noexec should disable LD_PRELOAD """
        args = self.args + ["--allowed=['echo','export']", "--path_noexec=''"]
        userconf = CheckConfig(args).returnconf()
        # verify that no alias was created containing LD_PRELOAD
        return self.assertNotIn('echo', userconf['aliases'])

    def test_26_checksecure_quoted_command(self):
        """ U26 | quoted command should be parsed """
        INPUT = 'echo 1 && "bash"'
        return self.assertEqual(sec.check_secure(INPUT, self.userconf)[0], 1)

    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)

    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)
Esempio n. 24
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)
Esempio n. 25
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)
Esempio n. 26
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 existance 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']]).returnconf()
            self.prompt = '%s:~$ ' % self.setprompt(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')
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf['strict']) == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict = self.conf['strict']) == 1:
            # see http://tldp.org/LDP/abs/html/exitcodes.html
            self.retcode = 126
            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 occureces 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 = get_aliases(self.g_line, self.conf['aliases'])

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

            if self.g_cmd == 'cd':
              if re.search('[;&\|]', self.g_line):
                # ignore internal cd function in case more than one command
                self.retcode = exec_cmd(self.g_line)
              else:
                # builtin cd function
                self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == 'export':
                self.retcode = self.export()
            # 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]
                self.g_arg = ' '.join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = 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)
Esempio n. 27
0
class ShellCmd(cmd.Cmd, object):
    """ Main lshell CLI class
    """
    def __init__(self,
                 userconf,
                 args,
                 stdin=None,
                 stdout=None,
                 stderr=None,
                 g_cmd=None,
                 g_line=None):
        if stdin is None:
            self.stdin = sys.stdin
        else:
            self.stdin = stdin
        if stdout is None:
            self.stdout = sys.stdout
        else:
            self.stdout = stdout
        if stderr is None:
            self.stderr = sys.stderr
        else:
            self.stderr = stderr

        self.args = args
        self.conf = userconf
        self.log = self.conf['logpath']

        # Set timer
        if self.conf['timer'] > 0:
            self.mytimer(self.conf['timer'])
        self.identchars = self.identchars + '+./-'
        self.log.error('Logged in')
        cmd.Cmd.__init__(self)
        if 'prompt' in self.conf:
            self.promptbase = self.conf['prompt']
            self.promptbase = self.promptbase.replace('%u', getuser())
            self.promptbase = self.promptbase.replace(
                '%h',
                os.uname()[1].split('.')[0])
        else:
            self.promptbase = getuser()

        self.prompt = '%s:~$ ' % self.promptbase

        self.intro = self.conf['intro']

        # initialize oldpwd variable to home directory
        self.oldpwd = self.conf['home_path']

        # initialize cli variables
        self.g_cmd = g_cmd
        self.g_line = g_line

        # initialize return code
        self.retcode = None

    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 existance 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!
        """

        # 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']]).returnconf()
            self.prompt = '%s:~$ ' % self.setprompt(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')
            sys.exit(0)
        if self.check_secure(self.g_line, self.conf['strict']) == 1:
            return object.__getattribute__(self, attr)
        if self.check_path(self.g_line, strict=self.conf['strict']) == 1:
            return object.__getattribute__(self, attr)
        if self.g_cmd in self.conf['allowed']:
            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
            self.g_line = re.sub('(\s|^)\$\?(\s|$)', ' %s ' % self.retcode,
                                 self.g_line)

            if type(self.conf['aliases']) == dict:
                self.g_line = get_aliases(self.g_line, self.conf['aliases'])
            self.log.info('CMD: "%s"' % self.g_line)
            if self.g_cmd == 'cd':
                self.retcode = self.cd()
            # builtin lpath function: list all allowed path
            elif self.g_cmd == 'lpath':
                self.retcode = self.lpath()
            # builtin lsudo function: list all allowed sudo commands
            elif self.g_cmd == 'lsudo':
                self.retcode = self.lsudo()
            # builtin history function: print command history
            elif self.g_cmd == 'history':
                self.retcode = self.history()
            # builtin export function
            elif self.g_cmd == 'export':
                self.retcode = self.export()
            # 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]
                self.g_arg = ' '.join(self.g_line.split()[1:])
                self.retcode = self.cd()
            else:
                self.retcode = os.system('set -m; %s' % 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 = ['', '', '']
        return object.__getattribute__(self, attr)

    def setprompt(self, conf):
        """ set prompt used by the shell
        """
        if conf.has_key('prompt'):
            promptbase = conf['prompt']
            promptbase = promptbase.replace('%u', getuser())
            promptbase = promptbase.replace('%h', os.uname()[1].split('.')[0])
        else:
            promptbase = getuser()

        return promptbase

    def lpath(self):
        """ lists allowed and forbidden path
        """
        if self.conf['path'][0]:
            sys.stdout.write("Allowed:\n")
            for path in self.conf['path'][0].split('|'):
                if path:
                    sys.stdout.write(" %s\n" % path[:-2])
        if self.conf['path'][1]:
            sys.stdout.write("Denied:\n")
            for path in self.conf['path'][1].split('|'):
                if path:
                    sys.stdout.write(" %s\n" % path[:-2])
        return 0

    def lsudo(self):
        """ lists allowed sudo commands
        """
        if self.conf.has_key('sudo_commands'):
            sys.stdout.write("Allowed sudo commands:\n")
            for command in self.conf['sudo_commands']:
                sys.stdout.write(" - %s\n" % command)
        return 0

    def history(self):
        """ print the commands history
        """
        try:
            try:
                readline.write_history_file(self.conf['history_file'])
            except IOError:
                self.log.error('WARN: couldn\'t write history ' \
                                   'to file %s\n' % self.conf['history_file'])
                return 1
            f = open(self.conf['history_file'], 'r')
            i = 1
            for item in f.readlines():
                sys.stdout.write("%d:  %s" % (i, item))
                i += 1
        except:
            self.log.critical('** Unable to read the history file.')
            return 1
        return 0

    def export(self):
        """ export environment variables """
        # if command contains at least 1 space
        if self.g_line.count(' '):
            env = self.g_line.split(" ", 1)[1]
            # if it conatins the equal sign, consider only the first one
            if env.count('='):
                var, value = env.split(' ')[0].split('=')[0:2]
                # expand values, if variable is surcharged by other variables
                try:
                    import subprocess
                    p = subprocess.Popen("`which echo` %s" % value,
                                         shell=True,
                                         stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE)
                    (cin, cout) = (p.stdin, p.stdout)
                except ImportError:
                    cin, cout = os.popen2('`which echo` %s' % value)
                value = cout.readlines()[0]

                os.environ.update({var: value})
        return 0

    def cd(self):
        """ implementation of the "cd" command
        """
        if len(self.g_arg) >= 1:
            # add wildcard completion support to cd
            if self.g_arg.find('*'):
                # get all files and directories matching wildcard
                wildall = glob.glob(self.g_arg)
                wilddir = []
                # filter to only directories
                for item in wildall:
                    if os.path.isdir(item):
                        wilddir.append(item)
                # sort results
                wilddir.sort()
                # if any results are returned, pick first one
                if len(wilddir) >= 1:
                    self.g_arg = wilddir[0]
            # go previous directory
            if self.g_arg == '-':
                self.g_arg = self.oldpwd

            # store current directory in oldpwd variable
            self.oldpwd = os.getcwd()

            # change directory
            try:
                os.chdir(os.path.realpath(self.g_arg))
                self.updateprompt(os.getcwd())
            except OSError, (ErrorNumber, ErrorMessage):
                sys.stdout.write("lshell: %s: %s\n" %
                                 (self.g_arg, ErrorMessage))
                return ErrorNumber
        else: