From 7b9b4bacc9efa579fcc9ca34a455b3cf6d22d2e5 Mon Sep 17 00:00:00 2001
From: Philip ABBET <philip.abbet@idiap.ch>
Date: Wed, 13 Sep 2017 14:31:35 +0200
Subject: [PATCH] Change the way UIDs are handled

---
 beat/backend/python/helpers.py         |  1 +
 beat/backend/python/scripts/execute.py | 94 ++++++++++++++++++--------
 2 files changed, 67 insertions(+), 28 deletions(-)
 mode change 100644 => 100755 beat/backend/python/scripts/execute.py

diff --git a/beat/backend/python/helpers.py b/beat/backend/python/helpers.py
index afb2382..40229b2 100755
--- a/beat/backend/python/helpers.py
+++ b/beat/backend/python/helpers.py
@@ -44,6 +44,7 @@ def convert_experiment_configuration_to_container(config, proxy_mode):
     'algorithm': config['algorithm'],
     'parameters': config['parameters'],
     'channel': config['channel'],
+    'uid': os.getuid(),
   }
 
   if 'range' in config:
diff --git a/beat/backend/python/scripts/execute.py b/beat/backend/python/scripts/execute.py
old mode 100644
new mode 100755
index 91e7931..4a130ed
--- a/beat/backend/python/scripts/execute.py
+++ b/beat/backend/python/scripts/execute.py
@@ -54,6 +54,8 @@ import sys
 import docopt
 import pwd
 import stat
+import simplejson
+import subprocess
 
 import zmq
 
@@ -69,6 +71,9 @@ class UserError(Exception):
     return repr(self.value)
 
 
+#----------------------------------------------------------
+
+
 def send_error(logger, socket, tp, message):
   """Sends a user (usr) or system (sys) error message to the infrastructure"""
 
@@ -98,6 +103,19 @@ def send_error(logger, socket, tp, message):
       logger.error('stopping 0MQ client anyway')
 
 
+#----------------------------------------------------------
+
+
+def close(logger, socket, context):
+    socket.setsockopt(zmq.LINGER, 0)
+    socket.close()
+    context.term()
+    logger.debug("0MQ client finished")
+
+
+#----------------------------------------------------------
+
+
 def main():
 
   """
@@ -113,6 +131,7 @@ def main():
   args = docopt.docopt(__doc__ % dict(prog=prog, version=version),
           version=version)
 
+
   # Sets up the logging system
   if args['--debug']:
     logging.basicConfig(format='[remote|%(name)s] %(levelname)s: %(message)s',
@@ -123,27 +142,6 @@ def main():
 
   logger = logging.getLogger(__name__)
 
-  # Attempt to change to an user with less privileges
-  try:
-    # First determine if the user exists. If not, none of the following lines will
-    # be executed
-    newuid = pwd.getpwnam('beat-nobody').pw_uid
-
-    # Next, ensure that the needed files are readable by the 'beat-nobody' user
-    access = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH
-
-    os.chmod(args['<dir>'], access)
-
-    for root, dirs, files in os.walk(args['<dir>']):
-      for d in dirs:
-        os.chmod(os.path.join(root, d), access)
-      for f in files:
-        os.chmod(os.path.join(root, f), access)
-
-    # Change the user
-    os.setuid(newuid)
-  except:
-    pass
 
   # Creates the 0MQ socket for communication with BEAT
   context = zmq.Context()
@@ -152,12 +150,52 @@ def main():
   socket.connect(address)
   logger.debug("zmq client connected to `%s'", address)
 
+
+  # Check the dir
+  if not os.path.exists(args['<dir>']):
+    send_error(logger, socket, 'sys', "Running directory `%s' not found" % args['<dir>'])
+    close(logger, socket, context)
+    return 1
+
+
+  # Create a new user with less privileges
+  with open(os.path.join(args['<dir>'], 'configuration.json'), 'r') as f:
+    cfg = simplejson.load(f)
+
+  retcode = subprocess.call(['adduser', '--uid', str(cfg['uid']),
+                             '--no-create-home', '--disabled-password',
+                             '--disabled-login', '--gecos', '""', '-q',
+                             'beat-nobody'])
+  if retcode != 0:
+    send_error(logger, socket, 'sys', 'Failed to create an user with the UID %d' % cfg['uid'])
+    close(logger, socket, context)
+    return 1
+
+
+  # Ensure that the needed files are readable by the new user
+  access = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH
+
+  os.chmod(args['<dir>'], access)
+
+  for root, dirs, files in os.walk(args['<dir>']):
+    for d in dirs:
+      os.chmod(os.path.join(root, d), access)
+    for f in files:
+      os.chmod(os.path.join(root, f), access)
+
+
+  # Change to the user with less privileges
   try:
+    os.setgid(cfg['uid'])
+    os.setuid(cfg['uid'])
+  except:
+    import traceback
+    send_error(logger, socket, 'sys', traceback.format_exc())
+    close(logger, socket, context)
+    return 1
 
-    # Check the dir
-    if not os.path.exists(args['<dir>']):
-      raise IOError("Running directory `%s' not found" % args['<dir>'])
 
+  try:
     # Sets up the execution
     executor = Executor(socket, args['<dir>'])
 
@@ -210,13 +248,13 @@ def main():
     return 1
 
   finally:
-    socket.setsockopt(zmq.LINGER, 0)
-    socket.close()
-    context.term()
-    logger.debug("0MQ client finished")
+    close(logger, socket, context)
 
   return 0
 
 
+#----------------------------------------------------------
+
+
 if __name__ == '__main__':
   sys.exit(main())
-- 
GitLab