Script pour GNU/Linux : lancer un logiciel X (graphique) avec un utilisateur n’ayant pas le droit
Il vous est probablement déjà arrivé, sous GNU/Linux, d’avoir besoin de lancer un de vos programmes ayant une interface graphique, comme Firefox, ou un programme louche que vous venez de télécharger, avec un autre utilisateur, sous votre session X actuelle, sans pour autant en ouvrir une autre.
Il y a plusieurs façons de faire cela. Par exemple se connecter avec ssh en localhost, avec l’option -X ou -Y, ou utiliser xhost. Le souci, c’est que le premier est un peu lent (par exemple, pour lancer un jeu vidéo). Quand au deuxième, il n’est pas terrible du côté de la sécurité (ah les souvenirs… Polluer les displays des autres ).
Une des solutions les plus intéressantes est d’exporter votre clé avec xauth, pour l’importer ensuite dans l’utilisateur qui vous intéresse ! Comme il faut entrer deux ou trois commandes pour faire cela manuellement, j’ai développé le programme sendxauth.py, permettant d’automatiser cela grâce à cette commande simple à mémoriser.
Un petit exemple pour clarifier les choses
Si mon explication n’a pas été claire, laisser moi vous donner un petit exemple.
Supposons que vous avez un programme louche sous la main, que vous devez tester à tout prix. Comme il risque de faire ce qu’il veut dans le répertoire de votre utilisateur principal, par exemple vous voler vos informations confidentielles (les fichiers de configuration de votre navigateur web, qui contiennent peut-être vos mots de passes, ou vos courriels personnels ou confidentiels archivés), il peut-être intéressant de lancer ce programme avec un utilisateur limité. Donc, au lieu de le lancer avec votre utilisateur principal, vous allez utiliser l’utilisateur hyper limité « cobaye », qui n’a le droit de rien faire dans votre système, à part toucher à ses fichiers.
Le souci c’est que, si vous vous loguez dans votre utilisateur hyper limité :
su -l cobaye
Et que vous lancez un programme graphique comme xterm (toujours en tant que « cobaye ») :
xterm
Vous aurez l’erreur :
xterm Xt error: Can't open display:
xterm: DISPLAY is not set
C’est un peu normal car l’utilisateur n’a le droit de rien faire dans votre display (sécurité).
Pour lui donner ce droit, vous devez utiliser sendxauth.py (le script de ce tutoriel, voir plus bas) qui va s’occuper de tout faire à note place :
sendxauth.py cobaye
Une fois que vous vous reloguerez sur « cobaye » :
su -l cobaye
Et que vous aurez déclaré les deux variables recommandées par sendxauth.py :
export DISPLAY=":0.0"
export XAUTHORITY="/home/cobaye/.Xauthority"
xterm se lancera sans aucun souci, en tant que « cobaye » !
Télécharger le script
Vous pouvez télécharger le script sendxauth.py ou le copier coller depuis :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2008 Asher256
#
# Contact : contact at asher256 dot com
# Website : http://blog.asher256.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with This program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
"""Send xauth key to different users."""
import sys
import os
import re
import pwd
from getopt import gnu_getopt, GetoptError
VERSION = '0.1'
SOURCE_USER = ''
DESTINATION_USER = ''
DISPLAY = ':0.0'
VERBOSE = False
DEV_NULL = ' >/dev/null 2>&1'
def vprint(string):
"""Print 'string' if --verbose is defined.
"""
if VERBOSE != False:
print string
def check_environment():
"""Check if all required environment variables are available.
"""
display = os.getenv('DISPLAY')
if display == None:
print 'DISPLAY environment variable is not declared.'
sys.exit(1)
else:
globals()["DISPLAY"] = display
def user_exists(user):
"""Return True if the user exists.
"""
try:
pwd.getpwnam(user)
except KeyError:
return False
else:
return True
def handle_arguments():
"""Handle options in the arguments (argv).
"""
try:
args = sys.argv[1:]
optlist = gnu_getopt(args, 'vh', ['help', 'verbose'])
except GetoptError:
print 'Error when parsing arguments.'
print "--help for more informations."
sys.exit(1)
if len(sys.argv) < 2:
print 'What\\'s the destination user ?'
print "--help for more informations."
sys.exit(1)
for user in optlist[1]:
if not user_exists(user):
print "The user '%s' doesn't exists." % user
sys.exit(1)
else:
globals()["DESTINATION_USER"] = user
break
for option, value in optlist[0]:
if option in ['-v', '--verbose']:
globals()['VERBOSE'] = True
globals()['DEV_NULL'] = ''
elif option in ['-h', '--help']:
print __doc__[0:-2]
print
print 'Usage: %s [OPTIONS] destination_user ' \\
% os.path.basename(sys.argv[0])
print
print "OPTIONS :"
print " -h, --help Show this help"
print " -v, --verbose Verbose mode"
print
sys.exit(0)
def commands_required(*cmd_list):
"""This function tests if all programs in
the arguments are available in the environment
variable 'PATH'.
"""
path = os.getenv('PATH')
if path != None:
path_list = path.split(os.pathsep)
else:
print "The environment variable PATH is not defined."
sys.exit(1)
for command in cmd_list:
error = True
for path in path_list:
command_path = os.path.join(path, command)
if os.access(command_path, os.X_OK):
error = False
break
if error:
print 'The command \\'%s\\' is not found.' % command
sys.exit(1)
def su_command_generator(command, user=''):
"""Convert a command to : su -c 'command' user and return that.
"""
command = re.sub(r"(['\\\\])", r"\\\\\\1", command)
command = 'su -c \\'' + command + '\\''
if user != '':
command += ' ' + user
return command
def send_xauth(destination_user, source_user=''):
"""Send the authentication to the destination user
If source_user is '', the source user is the actual user (automatically
detected).
"""
if source_user != '':
sys.stdout.write(source_user + '\\'s ')
print 'xauth key will be sent to', destination_user + '...'
print
auth_tmpfile = '/tmp/sendxauth' + str(os.getpid())
try:
# extract
command = 'xauth extract ' + auth_tmpfile + ' ' + DISPLAY
if source_user != '':
sys.stdout.write('You must enter ' + source_user + ' password : ')
command = su_command_generator(command)
vprint(command)
result = os.system(command + DEV_NULL)
if source_user != '':
print
if result != 0:
print 'Errors when extracting xauth key.'
sys.exit(1)
# chmod auth key
os.chmod(auth_tmpfile, 0777)
# merge
sys.stderr.write('You must enter ' + destination_user + ' password : ')
destination_home = pwd.getpwnam(destination_user)[5]
authfile = os.path.join(destination_home, '.Xauthority')
# masquer les ' et \\ dans authfile
command = su_command_generator('xauth merge ' + auth_tmpfile,
destination_user)
command = 'XAUTHORITY=\\'' + authfile + '\\' ' + command
vprint(command)
result = os.system(command + DEV_NULL)
if DEV_NULL != '':
print
if result != 0:
print 'Error when merging xauth key by ' + destination_user + '.'
sys.exit(1)
else:
print "xauthority is sent to %s !" % destination_user
print
print "You maybe must declare these shell variables before " + \\
"running a graphical program :"
print "export DISPLAY=\\"%s\\"" % DISPLAY
print "export XAUTHORITY=\\"%s/.Xauthority\\"" % \\
pwd.getpwnam(destination_user)[5]
finally:
try:
os.remove(auth_tmpfile)
except OSError:
print 'Warning: Cannot remove ' + auth_tmpfile + '...'
else:
vprint('Notice: ' + auth_tmpfile + ' deleted ')
if __name__ == '__main__':
try:
commands_required('xauth', 'su')
check_environment()
handle_arguments()
send_xauth(DESTINATION_USER, SOURCE_USER)
except KeyboardInterrupt:
print "Interrupted."
# vim:ai:et:sw=4:ts=4:sts=4:tw=78:fenc=utf-8