Qrexec--any way to know it's running a script?

There’s actually two questions here, one to do with the client, the other to do with dom0 when dom0 is the target of the call.

First Question:

On the client (making the qrexec-client-vm call), of course you name the service and target vm, and then pass it a “client” script whose job it is to echo command line parameters so the system on the other end of the call can read them one by one. (And of course dom0 will check to make sure the call is permitted.)

If I write some utility to run on a domU, this means I have two files, the utility itself as seen by the user (or calling script)–this one makes the qrexec call, and the client file (which I was taught should be in /usr/lib/qubes). I’d like to know if there is a way to consolidate them. I could, for instance, put both bits of code in the same file, and check (inside the file) whether I am being called by qrexec-client-vm as a client file. So a utility named my_util would look like:

#!/bin/bash
if < not being called by qrexec >; then # This is what I don't know how to do.
    #process command line parameters
    ...
    qrexec-client-vm  my.service  target-vm my_util param1 param2 param3
else
    # echo parameters then do that exec that 
    # redirects output over there back to here.
    echo $1
    echo $2
    echo $3
    exec cat >&$SAVED_FD_1
fi

So what’s the test for being called by qrexec? I expect if there’s a way to tell it’s some sort of environment variable that gets set.

Second Question:

For some reason, if I call notify-send on dom0 in some script being called as the result of a qrexec-client-vm with dom0 as the target, I have to set my display variable (understandably)…and that is not enough; then the notify-send call has to be done with sudo -u SteveC, making sure my username is the account name! (This is in spite of the fact that a whoami here will show I am already SteveC–I already tried checking that to see if I could distinguish why the script is being called.) If I don’t do that sudo call, the notify-send call hangs for a couple of minutes before notify-send throws an error.

If i run that same script from the command line on dom0 none of this is necessary. I can understand the bit about the display variable; if being called from some other system it would need to be set. But for the bit about having to sudo, 1) why is this and 2) is there a good way to put code in the script to check to see whether it’s being called because of a qrexec call, or not?

1 Like

qrexec-client-vm --help calls it the “local program”, so I’ll use that term.

It’s more general. You (optionally!) pass a local program in situations where you don’t want qrexec-client-vm to use the ambient stdin/stdout to communicate with the destination side and would rather have one locally spawned process do the communication via its standard streams.

If you just need to frame the ambient stdin in some extra data, this can be done without a local program. For example, the my_util script could be rewritten as

{
    echo param1
    echo param2
    echo param3
    cat
} | qrexec-client-vm target-vm my.service

or in this case even

prefix="param1
param2
param3
"

qrexec-client-vm --prefix-data="$prefix" target-vm my.service

No, the local program can be anywhere. It’s passed as an absolute path.

If $SAVED_FD_0 is set you’re almost certainly in a local program. (I’m not aware of any other situation where this variable would be set.)

Whenever I’m wondering about this kind of thing, I run env | sort (redirected to a new file) both inside and outside the somehow special environment and then compare the two files:

$ env | sort >/tmp/a
$ qrexec-client-vm @dispvm qubes.VMShell /bin/sh -c 'env | sort >/tmp/b'
$ diff -u /tmp/a /tmp/b

Setting $DBUS_SESSION_BUS_ADDRESS as well seems to fix this. No idea why the dom0 RPC environment is so bare bones.

You mean the dom0 RPC service script in /etc/qubes-rpc/? It could check if $QREXEC_REMOTE_DOMAIN is set. (There would be false positives if the RPC service script was in a VM, but it’s not a problem in dom0.)

3 Likes

Thank you. This has been very helpful!

“local program”…will start using that. (my local program files all have “client” in their name but the hope is to be able to get rid of those files)

One point of clarification; I didn’t think it was required to place these files in /usr/lib/qubes, but I did think it was some sort of convention. I’m happy not to do so, it’s just another doggone directory I have to remember (like the ones where desktop files and icon files go…I always have to go look those up).

You’ve shown me a few approaches for combining local programs with whatever is calling them, ones a LOT less clunky than the idea I had! (I had no idea about --prefix-data just for instance.) But I am unsure what you mean by “situations where you don’t want qrexec-client-vm to use the ambient stdin/stdout to communicate with the destination side and would rather have one locally spawned process do the communication via its standard streams.” What is the actual difference between the two scenarios?

To describe what I’m actually doing, my client, I mean local file looks like this (I’ll assume it’s named “my_local_service”):

    echo $1
    echo $2
    echo $3
    exec cat >&$SAVED_FD_1

And I call it something like this from my_service on domU

Output=$(qrexec-client-vm my.service target-vm my_local_service param1 param2 param3)
Status=$?

and of course my.service will be invoked on dom0 and it should read three lines of input and thereby get param1, param2 and param3

By setting Output as I did I have a way to prevent it going directly to the standard output on the domU; it’s a decent way for me to pass error messages back, then output them to send-notify if there’s an error (otherwise I’ll likely never see it if I ran from a desktop icon). And of course Status is equal to whatever my.service exited with.

I’m gathering if I use one of your methods here, something would change, but what, and how?

As for the dom0 end, setting both the DISPLAY and DBUS_SESSION_BUS_ADDRESS variables in my.service on dom0 and exporting them solves the problem. (I basically wrote a shell around send-notify that makes sure it loads icons, etc.; and made it available to domU since the send-notify that is on the domU has totally different behavior, making an inconspicuous looking blue rectangle in a totally different part of the screen.) I call that wrapper from dom0 and via qrexec. And sometimes from a different qrexec call–there I have to remember to set those variables before calling notify-send either directly or via the wrapper. I can check -z DBUS_SESSION_BUS_ADDRESS before setting it in case putting all such calls in service.service files is not good enough.

Thanks again! Very helpful!

2 Likes

Me neither, until 3 days ago :slight_smile:

Oh I just meant that without a local program, qrexec-client-vm inherits the existing stdin/stdout from whatever is calling it. Which may be inconvenient for use cases where there already is an existing client-like program communicating over stdin/stdout. Without the dedicated local program syntax, one would have to resort to something like:

$ mkfifo f
$ local_program <f | qrexec-client-vm vm service >f

(Or something even more annoying than that, if qrexec-client-vm isn’t being run from a shell but e.g. from a C program.)

If the goal is to allow VMs to send more or less arbitrary notifications to a notification daemon across a trust boundary (in this case: VM → dom0), it’s hard to do that securely. For example, if you’re using notify-send on the destination side it will interpret C-like escape sequences, and then the notification daemon will interpret XML-like markup…

Are you maybe looking for

There’s a dom0 package (qubes-notification-daemon) and VM package (qubes-notification-agent). Needed some manual policy configuration and I had to mask the VM’s normal notification daemon, but other than that it appears to be functional.

3 Likes

Will look into that Centralized Tray Notifications. Thanks again for that and your explanation!

2 Likes