Regarding an "earlier" variant of `pre-xfer exec` [feature request and proposed patch]

Ciprian Dorin Craciun ciprian.craciun at gmail.com
Fri Nov 8 15:59:46 UTC 2019


(I think the subject is quite descriptive;  however for use-cases and
details see bellow after the mention of the old conversation and the
patch.)


Searching the mailing list about this topic yields an old conversation
about this from 2008:
* https://lists.samba.org/archive/rsync/2008-November/022148.html
* https://lists.samba.org/archive/rsync/2008-November/022150.html

I'll quote Wayne's reply:
> That is not currently possible because rsync wants to tell the pre-xfer
> process about the options that the client sent (such as what module is
> being requested), and that conversation happens after the chroot is all
> setup and the protocol data is already flowing.  It might be possible to
> twiddle this, but it could be less secure, so I don't wish to change it.
>
> Your best bet is to make the root of the module a local dir and mount a
> subdir inside that local directory based on the pre-xfer data.


Unfortunately this proposed hack doesn't work (at least not in the
latest 3.1.3), because the `chdir` includes the sub-path inside the
module.
(I.e. there isn't an `chdir(module_path) && execve(prexfer) &&
chdir(arg_path)` but a single `chdir(module_and_arg_path) &&
execve(prexfer)`.)


Therefore I've tried to patch `rsync` myself, mainly by copy-pasting
the code related to `pre-xfer exec` option:
* https://github.com/cipriancraciun/rsync/commit/1f85c5f596542ed878d09a60e55ea0279346802b

Please note this is the first time I see the `rsync` code-base, and
haven't done an indepth analysis.  I haven't made any audit regarding
the security implications, but given that the patch is pretty small,
and most of the code is copy-paste of an existing feature, I would
assume it is pretty safe.


So, going backwards, I've searched for a feature that would allow one
to run a process **before** `rsync` attempts any `chroot`, `chdir`,
`setuid`, etc.
Unfortunately `pre-xfer exec` executes after all of those...

My particular use-case was exposing via `rsync` an OpenAFS volume
which requires (AFS) authentication.  Without going to in-depth,
OpenAFS introduces (in the kernel) a PAG (`Process Authentication
Group`) context, which allows the same user (or multiple users) to use
various credentials.  Therefore each new `rsync` process will start
with a new PAG, and before accessing any files, one must call the
`klog` tool to authenticate.  However as said above, one can`t use
`pre-xfer exec` because that executes after `chdir` which fails.

For reference:
* https://docs.openafs.org/Reference/1/pagsh.html
* https://github.com/openafs/openafs/blob/master/src/sys/setpag.c
* `k_setpag` --
http://manpages.ubuntu.com/manpages/bionic/man3/krb_afslog.3.html


Other use-cases for such a feature:
* as the original poster of the thread from 2008 said, he wanted to
mount an encrypted file-system;
* one could expose a Git worktree as a `rsync` module and use the
"early" hook to make sure no one is using the repository (thus a lock
it or wait), then use the `post-xfer` to commit;  (granted one could
use `pre-xfer` for this, but it opens the possibility of
race-conditions when one uses a sub-folder instead of the whole
module;  i.e. someone just deletes the folder another one tries to
copy-in;)
* move the parent `rsync` process into a separate Linux mount
namespace / control group (before doing anything else);
* any other OS-related trick that should happen before `rsync` starts
doing anything;


Also related to this topic, and back to my OpenAFS use-case, there is
an extra requirement (that I've solved differently, via `systemd`
`inetd` mode):  in order to create the new PAG (else it inherits the
PAG of the `root` user), one has to run each `rsync` process "wrapped"
by `pagsh`.  Therefore an extra "hook" might be `wrapper exec` which
implements the following: for each new connection (either in daemon
mode or inetd mode), instead of just forking, execute that wrapper and
append to it the required arguments, and the wrapper should make the
setup, then run the given command and arguments.  (I.e. just like
`env` does.)

Hope this helps,
Ciprian.



More information about the rsync mailing list