diff --git a/src/formconfig.py b/src/formconfig.py index 2ab5392..57894a3 100644 --- a/src/formconfig.py +++ b/src/formconfig.py @@ -8,8 +8,17 @@ import logging import stat import os import subprocess +import pwd +import grp +def run_as(uid, gid, groups): + def set_acc(): + os.setgroups(groups) + os.setgid(gid) + os.setuid(uid) + return set_acc + class FormConfigError(Exception): """ Default error for FormConfig errors @@ -77,6 +86,7 @@ class FormConfig(object): callback should be written. The output of the script is hooked up to the output, depending on the output type. """ + # FIXME: This doesn't really belong in FormCOnfig. form = self.get_form_def(form_name) # Validate params @@ -90,26 +100,54 @@ class FormConfig(object): for key, value in form_values.items(): env[key] = str(value) + # Get the user uid, gid and groups we should run as + pw = pwd.getpwnam(form.run_as) + gr = grp.getgrgid(pw.pw_gid) + groups = [g.gr_gid for g in grp.getgrall() if pw.pw_name in g.gr_mem] + uid = pw.pw_uid + gid = pw.pw_gid + msg = "Running script as user={0}, gid={1}, groups={2}" + self.log.info(msg.format(pw.pw_name, gr.gr_name, str(groups))) + if os.getuid() != 0: + self.log.error("Not running as root! Running as different user " + "will probably fail!") + # If the form output type is 'raw', we directly stream the output to # the browser. Otherwise we store it for later displaying. if form.output == 'raw': - proc = subprocess.Popen(form.script, shell=True, - stdout=stdout, - stderr=stderr, - env=env, - close_fds=True) - stdout, stderr = proc.communicate(input) - return proc.returncode + try: + proc = subprocess.Popen(form.script, shell=True, + stdout=stdout, + stderr=stderr, + env=env, + close_fds=True, + preexec_fn = run_as(uid, gid, groups)) + stdout, stderr = proc.communicate(input) + return proc.returncode + except OSError as err: + self.log.exception(err) + stderr.write(str(err) + '. Please see the log file.') + return -1 else: - proc = subprocess.Popen(form.script, shell=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, - close_fds=True) - stdout, stderr = proc.communicate() - return { - 'stdout': stdout, - 'stderr': stderr, - 'exitcode': proc.returncode - } + try: + proc = subprocess.Popen(form.script, shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + close_fds=True, + preexec_fn = run_as(uid, gid, groups)) + stdout, stderr = proc.communicate() + return { + 'stdout': stdout, + 'stderr': stderr, + 'exitcode': proc.returncode + } + except OSError as err: + self.log.exception(err) + return { + 'stdout': '', + 'stderr': 'Internal error: {0}. Please see the log ' \ + 'file.'.format(str(err)), + 'exitcode': -1 + } diff --git a/src/formdefinition.py b/src/formdefinition.py index f8525b4..38a8175 100644 --- a/src/formdefinition.py +++ b/src/formdefinition.py @@ -19,7 +19,7 @@ class FormDefinition(object): """ def __init__(self, name, title, description, fields, script, output='escaped', hidden=False, submit_title="Submit", - allowed_users=None): + allowed_users=None, run_as='nobody'): self.name = name self.title = title self.description = description @@ -29,6 +29,7 @@ class FormDefinition(object): self.hidden = hidden self.submit_title = submit_title self.allowed_users = allowed_users + self.run_as = run_as self.validate_field_defs(self.fields) diff --git a/src/scriptform.py b/src/scriptform.py index 3ae8ab3..8c5f01d 100755 --- a/src/scriptform.py +++ b/src/scriptform.py @@ -115,7 +115,8 @@ class ScriptForm(object): output=form.get('output', 'escaped'), hidden=form.get('hidden', False), submit_title=form.get('submit_title', 'Submit'), - allowed_users=form.get('allowed_users', None)) + allowed_users=form.get('allowed_users', None), + run_as=form.get('run_as', 'nobody')) ) form_config = FormConfig(