The Grumpy Troll

Ramblings of a grumpy troll.

Synology NAS & rsync

I own a Synology DS413j NAS (home fileserver, four disks); this is mostly a rather nice box, albeit with some quirks.

Some quirks might drive me away from buying a replacement box from this manufacturer; I am perplexed that to fix two-factor authentication sign-on, using a locally generated TOTP code, I had to clear cookies for Google. This is a home box and there should be no third-party tracking cookies for how I access devices within my own household.

Other quirks are just annoying.

One which had bothered me was the inability to use rsync to synchronize content to a dedicated part of the filesystem which I wanted to reserve for this usage. Permission denied, please try again. errors. So, today I got around to debugging what was happening.

I was able to get the source-code for GPL-compliance from an ugly dump; the files found inside https://sourceforge.net/projects/dsgpl/files/Synology%20NAS%20GPL%20Source/4458branch/ are variants of the code, with embedded binary-blobs which are CPU-specific. Using http://forum.synology.com/wiki/index.php/What_kind_of_CPU_does_my_NAS_have I was able to figure out that I should download synogpl-4458-6281.tbz. This is a dated release, but it was close enough to current to let me figure out what was happening.

The problem is not with rsync, but with custom patches to OpenSSH.

The above, latest, release has code for OpenSSH 5.8p1. The version of the firmware which I’m running right now supplies OpenSSH_6.6p2-hpn14v4. Since OpenSSH is under a BSD-style license (mostly; there’s a whole bunch of different licenses) there’s no obligation upon the vendor to supply updated source code. Well, what I have got me “close enough”. As to the vendor-added code snippets which I cite below: there’s no explicit license on these, but the download is in a file which is named starting synogpl, so the two reasonable assumptions are (1) that the code is licensed under the GPL and I can quote it; or (2) that the code is licensed under the same BSD license as the bulk of OpenSSH, and I can quote it.

The OpenSSH file session.c patches do_child(); the code of interest is within an #ifdef MY_ABC_HERE guard.

1864         } else if (IsRsyncReq(command)){
1865                 SSHCmd = REQ_RSYNC;
...
1874         if (REQ_RSYNC == SSHCmd) {
1875                 pw = SYNOChgValForRsync(pw);
1876         }
1877         if (!SSHCanLogin(SSHCmd, pw)) {
1878                 goto Err;
1879         }
1880         goto Pass;
1881
1882 Err:
1883         fprintf(stderr, "Permission denied, please try again.\n");
1884         exit(1);

with earlier:

1625 static int IsRsyncReq(const char *szCmd)
1626 {
1627         if (!szCmd) {
1628                 return 0;
1629         }
1630
1631         if (!strncmp(szCmd, "rsync", strlen("rsync"))){
1632                 if (HasNotAllowChar(szCmd)) {
1633                         return 0;
1634                 }
1635                 return 1; //command with rsync but without ';','|','`'
1636         }
1637
1638         return 0;
1639 }

While I haven’t yet untangled why I am getting permission denied when the command is rsync, I have found an easy work-around: log into the NAS as root, run cp /usr/syno/bin/rsync /usr/local/bin/r2sync and then on the client invoke rsync --rsync-path=/usr/local/bin/r2sync.

This is what happens when “security” is implemented badly. On the bright side, I can get back the access I need to my own property.

I think that the cause of the error lies in some code change not included in this old codedump.

Avoiding IsRsyncReq() returning true is sufficient to log in, IsRsyncReq() is only called in one place, the result is stored in a function-local automatic variable, and IsRsyncReq() has no side-effects (yes, I also checked HasNotAllowChar()).

The SYNOChgValForRsync() mutates the pw_shell and pw_dir fields of the pw struct pointer; it only does so if not root or admin and rsync doesn’t work as root@nas either.

This leaves two possible locations for the problem: in SSHCanLogin() or in code not in this codedump.

The SSHCanLogin() is a bit tangled with the synology guards and added functions in, but I’ve similarly traced through the visible paths and can’t see why that would lead to rejection.

Thus: rsync is being explicitly matched in Synology’s vendor-patched OpenSSH; when it matches, some code which I believe is not in their code-dump is rejecting access (or, it’s a bug too subtle for me to catch); avoiding letting the name-based matching succeed lets rsync work.

-The Grumpy Troll

Categories: synology NAS OpenSSH SSH rsync