Scripts Launched from Udev Do Not Have Display Access Anymore

Scripts launched from udev do not have DISPLAY access anymore?

Ok, I'm writing this answer to try and clarify the security model of the X server, as I understand it. I'm not an expert on the subject, so I may have got some (many?) things wrong. Also, many things are different in different distributions, or even different versions of the same distribution, as the OP noted.

There are two main ways to get authorized to connect to the X server:

  • The xhost way (Host Access): The server maintains a list of hosts, local users, groups, etc. that are allowed to connect to the server.
  • The xauth way (Cookie based): The server has a list of randomly generated cookies, and anybody showing one of these cookies will be granted access.

Now, the distribution specific stuff...

When the X server is launch by the start-up system, it is usually passed a command line of the form -auth <filename>. This file contains a list of initial cookies to be used for authorization. It is created before the X server is run using the xauth tool. Then just after the X server, the login manager is launch, and it is instructed to read the cookie from this same file, so it can connect.

Now, when user rodrigo logs in, it has to be authorized to connect to the server. That is done by the login manager, and it has two options:

  • It does the equivalent to: xhost +si:localuser:rodrigo.
  • It generates another cookie, adds it to the server and passes it to the user. This passing can be done in two ways:
    • It is written in the file $HOME/.Xauthority (home of the new user).
    • It is written somewhere else (/var/run/gdm/auth-for-rodrigo-xxxx) and the environment variable XAUTHORITY is set to the name of that file.

Also, it can do both things. Some login managers even add the root user to the list of authorized users by default (as if xhost +si:localuser:root).

But note that if you are not authorized to connect to the X server, you cannot add yourself to the list (running xhost + for example). The reason is the same as why you cannot open a house doof from the outside without a key... That's true even if you are root!

Does it mean that the root user cannot connect to the server? Absolutely not! But to get to that first you have to know how is the logged user configured to connect to the server. For that run as the logged user:

$ xhost

It will show a message and the list of authorized users, hosts or groups, if any:

access control enabled, only authorized clients can connect
SI:localuser:rodrigo

Then run:

$ echo $XAUTHORITY

To see where the authorization file is saved. If it is empty, then it will be ~/.Xauthority. Then:

$ xauth list :0

To see the list of your authorized cookies.

Now, if there are any cookie in the server, the root user should be able to connect making the XAUTHORITY environment variable point to the right cookie file. Note that in many setups, the cookie of the login manager is also kept around. Just look for it!

Another possibility for root access is to modify the Xsession files to add the command xhost +si:localuser:root and get permanent access. The details vary with the particular program used, but for gdm you would simply add an executable script in /etc/gdm/Init/ with the xhost command and it will be run automatically in the next boot.

PS: You can check your root access to the X server with sudo -i, but note that some sudo configurations may keep the DISPLAY, XAUTHORITY or HOME variables and modify the results of the tests.

EXAMPLE: This script should be able to connect you to the X server as root

export DISPLAY=:0
export XAUTHORITY=`ls /var/run/gdm/auth-for-gdm-*/database`
xrandr #just for show

Naturally, the path for the XAUTHORITY variable will depend on what login manager you are using (greeter). You can use the user file (you say it is in /home/redsandro/.Xauthority but I'm not so sure). Or you can use the greeter cookie. To get the greeter cookie you can use the following command:

$ pgrep -a Xorg

Which in my system gives:

408 /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-gDg3Ij/database -seat seat0 -nolisten tcp vt1

So my file is /var/run/gdm/auth-for-gdm-gDg3Ij/database. The gDg3Ij is random and changes every time the server is restarted, that's why the ls ... trick.

The nice thing of using the GDM cookie instead of the user is that it does not depend on the user logged in. It will even work with no user at all!

UPDATE: From your latest comment I see that your X server command is:

/usr/bin/X :0 -audit 0 -auth /var/lib/mdm/:0.Xauth -nolisten tcp vt8

So there is the name of the cookie used to start the login manager. If I'm correct, that should be available all the time, if you are able to read the file. And you are root, so, the following lines should be enough to get you access to the display as root:

export DISPLAY=:0
export XAUTHORITY=/var/lib/mdm/:0.Xauth

zenity --info --text 'Happy New Year'

notify-send not working (in script) executed from udev

The main problem is, that the udev-rule will not run in any xorg-related environment per default, thus not knowing which DISPLAY to use. Therefore it will always fail, if You want to echo something into a terminal like a gnome-terminal for example. The script, which shall be executed on the udev-rule-match, must prior to any ui-related execution first export the DISPLAY.
This is done via

export DISPLAY=:0

I assume, that this also will be the problem, and notify-send will just run against the wall.

I am actually also playing with udev-rules, and I managed it to work, though i am acting as root, similar to my answer and this one found already here :

https://unix.stackexchange.com/questions/80882/udev-running-a-shellscript-that-accesses-an-x-display

And also here

Scripts launched from udev do not have DISPLAY access anymore?

You might want also to check zenity. Very helpful for small notifications

Conditionless GOTO in udev rules (and Medion RC-0617)

Indeed, the second line (GOTO="end") was just ignored. It took me some time to figure that out, but the solution is actually pretty simple:

If there are no conditions to match against, udev treats the rule as "always not matching" and therefore does not execute the GOTO at all.

My working udev rules file looked like this:

# Test if the wanted dongle is a parent device
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f2", ATTRS{idProduct}=="0618", GOTO="match"
# If not, skip the next 3 rules. The test against SUBSYSTEM=="hidraw" is there to produce a rule match
SUBSYSTEM=="hidraw", GOTO="end"
LABEL="match"
# Those 3 rules actually assign the right symlink depending on the bInterfaceProtocol property.
# Note that ALL of those rules contain the SUBSYSTEM=="hidraw" check, because the GOTO in the second line
# does not get executed for non-hidraw devices and the rules get evaluated for any non-hidraw device.
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="01", SYMLINK="mdremote0", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="00", SYMLINK="mdremote1", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="02", SYMLINK="mdremote2", MODE="0666"
LABEL="end"

This turned out to work fine. It can still be improved by providing a better match rule for the GOTO="end" statement, but I left it that way.



Related Topics



Leave a reply



Submit