Espace d'Asher256

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, laissez 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 -*-
#
# Website : https://www.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




Déjà 15 commentaires dans “Script pour GNU/Linux : lancer un logiciel X (graphique) avec un utilisateur n’ayant pas le droit”
  1. NicoA380

    —–BEGIN PGP SIGNED MESSAGE—–
    Hash: SHA1

    Hop, un script de plus dans mon /usr/local/bin !

    C’est vrais que d’habitude je fais ssh -X. Il ne faudra pas que j’oublie que j’ai ton script sous le coude la prochaine fois.
    —–BEGIN PGP SIGNATURE—–
    Version: GnuPG v1.4.6 (GNU/Linux)
    Comment: http://getfiregpg.org

    iD8DBQFIus4mN5vSUeC+7GIRAg/tAJ9SHBTQhibunc4xaZiyxgKKLM77TQCfQfbW
    rEoP84ZFQ+/+HyB/sqNrbOg=
    =5Nk9
    —–END PGP SIGNATURE—–

  2. Asher256

    @NicoA380: ^_^

    @Lupus: intéressant. Je ne connaissais pas cette façon d’utiliser xhost. Je viens de jeter un oeil sur le man de xhost pour en savoir plus…

    Mais c’est curieux ; La commande donne chez moi le droit à tous les utilisateurs locaux d’utiliser mon X O.o .

    J’ai fait : xhost ‘+local:cobaye’

    Cela m’a donné :
    « non-network local connections being added to access control list »

    C’est donc positif. Ensuite, j’ai essayé xterm en tant que cobaye, cela a marché. En tant que www-data et postgres, cela a marché, et c’est la même chose avec les autres utilisateurs non-root.

    Une idée ?

    (si c’est cela le fonctionnement, autoriser tous les utilisateurs locaux, alors cela ne peut remplacer le script. Sinon, c’est excellent)

  3. Asher256

    > Quel misérable je suis. J’étais convaincu que
    > c’était suffisant. Comme quoi, c’est vraiment
    > quand on est persuadé d’être en sécurité
    > qu’on l’est le moins 😉

    En effet !

    > Après recherches, il semblerait que ceci soit plus restrictif :
    >
    > xhost +si:localuser:vielfrasser

    Apparemment, cela marche bien… (je viens de tester)

    C’est donc une bonne alternative à cette solution 😉 . Reste à savoir s’il y a un avantage à utiliser l’une ou l’autre.

    Merci pour ton excellente participation !

    > Mais comme souvent, nous autres pauvres
    > programmeurs nous rendons parfois compte qu’une
    > semaine de travail est réduite à néant par
    > l’existance d’un outil meilleurs et préexistant 😉

    😀

  4. dorian

    Bah moi je fais un « su » et un autre « su login » après si besoin de passer sur un autre user dans mon term puis je lance les programmes directement comme ça…
    Bon biensur pour ça il faut remettre un pass au root, ce qui est facilement faisable avec « sudo passwd » qui vous demandera votre pass user puis le pass root.

  5. Robin Millette

    J’utilise tout simplement « sux ». Un vieux (2001) script bash disponible dans Debian par exemple. http://fgouget.free.fr/sux/

    Mais merci pour ce script python 🙂

  6. Richard

    Et un « gksudo -u utilisateur commande  »
    N’est-il pas plus simple ?

  7. Richard

    Ok,
    Mais installer sudo est relativement simple, dailleurs, toutes mes débian ont sudo également :D.

  8. Richard

    Pourquoi c’est un trou de secu ?

  9. Peck

    Les commentaires sont-ils modérés a priori ou a posteriori ?
    Il n’y a pas de messagelorsqu’on poste un commentaire et il est difficile de savoir d’où cela vient.

  10. Peck

    (Lupus Michaelis) si tu vas par là, su aussi permet à n’importe que utilisateur d’obtenir les droits root.

    Sudo est configurable et permet de régler qui a accès à quoi et comment, et ce bien plus finement que su.

  11. Richard

    Heu, le mot de passe avec sudo, on est pas obligé de le retapé pendant un labs de temps (assez cours) sur le MEME terminal utilisé (si j’en change, ou si je le ferme et le réouvre, il faut me réauthentifier).

    De plus, cela aussi me semble t il est paramétrable.

  12. Asher256

    Pour info, il est possible de configurer sudo pour qu’il ne mémorise pas le mot de passe pendant quelques secondes.

    Il est aussi possible d’affiner ce qui est autorisé dans sudo (par exemple, lui dire avec une expression rationnelle : « tu as le droit de lancer useradd en tant que root avec un seul argument contenant des caractères alpha-numériques, sans aucun espace, mis à part celui qui sépare useradd et le premier argument »).

    Disons qu’un sudo avec une configuration par défaut (d’Ubuntu par exemple) pourrait être une faille de sécurité. Mais bien configuré, cela diminue les chances de tomber dans le « piège » dont on a fait allusion dans les commentaires ^_^ .

  13. Peck

    Par défaut, sudo ne te donne aucun droit.
    Sur Ubuntu, par défaut sudo donne les droit root (avec mot de passe) à l’utilisateur qui a installé le système. Il faudrait pas croire que les gens qui font les distribs sont des cons.

  14. Asher256

    Il n’y a personne qui a dit le contraire Peck 🙂 .

  15. dorian

    euh au risque de paraitre con… l’intérêt de ma technique était de ne pas avoir besoin d’entrer le pass de l’utilisateur que tu veux prendre (pour les comptes sans pass), pas spécialement de se mettre en root lol.

    Biensur tu peux passer directement ton user au « su » si tu as un pass sur le user.

    Et je ne comprends pas pourkoi c’est stupide puisk’au final le shell est bien à l’utilisateur et pas au root. Pourrais tu m’expliquer où est le problème s’il te plait?