For future reference: after way too many hours researching and debugging this issue, I finally discovered the root cause.
The OpenSSH version used by Synology is a highly customized version, that does not behave like the original code. It has lots of hacks and ad-hoc customizations - e.g., additional checking before accepting a login to see if the SSH service is enabled within the web interface, or stripping special chars (;, |, ') from rsync commands, or... wait for it... avoiding regular users to use a shell different than /bin/sh or /bin/ash. Yeah, hard coded within the binary.
Here's the piece of code from OpenSSH 5.8p1, as distributed by Synology on their source code (DSM4.1 - branch 2636), file session.c
:
void do_child(Session *s, const char *command)
{
...
#ifdef MY_ABC_HERE
char szValue[8];
int RunSSH = 0;
SSH_CMD SSHCmd = REQ_UNKNOWN;
if (1 == GetKeyValue("/etc/synoinfo.conf", "runssh", szValue, sizeof(szValue))) {
if (strcasecmp(szValue, "yes") == 0) {
RunSSH = 1;
}
}
if (IsSFTPReq(command)){
SSHCmd = REQ_SFTP;
} else if (IsRsyncReq(command)){
SSHCmd = REQ_RSYNC;
} else if (IsTimebkpRequest(command)){
SSHCmd = REQ_TIMEBKP;
} else if (RunSSH && IsAllowShell(pw)){
SSHCmd = REQ_SHELL;
} else {
goto Err;
}
if (REQ_RSYNC == SSHCmd) {
pw = SYNOChgValForRsync(pw);
}
if (!SSHCanLogin(SSHCmd, pw)) {
goto Err;
}
goto Pass;
Err:
fprintf(stderr, "Permission denied, please try again.\n");
exit(1);
Pass:
#endif /* MY_ABC_HERE */
...
}
As you can imagine, the IsAllowShell(pw)
was the culprit:
static int IsAllowShell(const struct passwd *pw)
{
struct passwd *pUnPrivilege = NULL;
char *szUserName = NULL;
if (!pw || !pw->pw_name) {
return 0;
}
szUserName = pw->pw_name;
if(!strcmp(szUserName, "root") || !strcmp(szUserName, "admin")){
return 1;
}
if (NULL != (pUnPrivilege = getpwnam(szUserName))){
if (!strcmp(pUnPrivilege->pw_shell, "/bin/sh") ||
!strcmp(pUnPrivilege->pw_shell, "/bin/ash")) {
return 1;
}
}
return 0;
}
No wonder why I was experiencing such an odd behavior. Only shells /bin/sh and /bin/ash would be accepted for users different than root or admin. And this regardless of the uid (I had tested also making joeuser uid=0, and it didn't work. Now it's obvious why).
Once identified the cause, the fix was easy: just remove the call to IsAllowShell(). It took me a while to get the right configuration to cross-compile openssh and all its dependencies, but it worked well in the end.
If anyone is interested in doing the same (or trying to cross-compile other kernel modules or binaries for Synology), here's my version of Makefile. It was tested with OpenSSH-5.8p1 source, and works well with models running Marvell Kirkwood mv6281/mv6282 CPU (like DS212+). I used a host running Ubuntu 12.10 x64.
Bottom line: bad practice, terrible code, and a great example of what not to do. I understand sometimes OEMs need to develop special customizations, but they should think twice before digging too deep. Not only this results in unmaintainable code for them, but also creates all sorts of unforeseen issues down the road. Thankfully GPL exist to keep them honest - and open.
You cannot directly execute a file that does not have the execute permission bit set. Using sudo
will allow you to (attempt to) execute a file that has any execute bit set at all, but if no execute bit is set, you will get "permission denied", no matter who you run as.
You can check whether git thinks the file permissions are different from what they should be by running git status
- it will show files as modified if they changed permissions; if that's it, you can fix it as described on stackoverflow.
If git thinks everything's fine, but permissions between your source and your clone repository differ, maybe you're using some odd filesystem that's confusing git's notion of what permissions the files should have, or maybe you have a git config option set (see an older answer to the same question). Whatever the cause, you need to do the same thing: copy the original permissions and apply them to your clone; this was previously answered here.
Best Answer
Do not run the command you are trying to run
If you try to redirect output from sed back into the same file it will blank it out, deleting all of the files contents. Try something like this:
You get permission denied because the redirection part of the command is not run via sudo, but run as your normal user.
The first command works because you are simply reading the first file and writing to a file you own, so you regular user could do that.