Soluzione 1:
Per riferimento futuro:dopo troppe ore di ricerca e debug di questo problema, ho finalmente scoperto la causa principale.
La versione OpenSSH utilizzata da Synology è una versione altamente personalizzata, che non comportarsi come il codice originale. Ha molti hack e personalizzazioni ad hoc, ad esempio controlli aggiuntivi prima di accettare un accesso per vedere se il servizio SSH è abilitato all'interno dell'interfaccia web o l'eliminazione di caratteri speciali (;, |, ') dai comandi rsync o... aspetta... evitare che gli utenti normali utilizzino una shell diversa da /bin/sh o /bin/ash . Sì, hardcoded all'interno del binario.
Ecco il pezzo di codice di OpenSSH 5.8p1, come distribuito da Synology sul loro codice sorgente (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 */
...
}
Come puoi immaginare, il IsAllowShell(pw)
era il colpevole:
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;
}
Non c'è da stupirsi perché stavo sperimentando un comportamento così strano. Solo le shell /bin/sh e /bin/ash sarebbero accettate per utenti diversi da root o amministratore . E questo indipendentemente dall'uid (avevo provato anche a fare joeuser uid=0 e non ha funzionato. Ora è ovvio perché).
Una volta identificata la causa, la soluzione è stata semplice:basta rimuovere la chiamata a IsAllowShell() . Mi ci è voluto un po' per ottenere la giusta configurazione per la compilazione incrociata di openssh e di tutte le sue dipendenze, ma alla fine ha funzionato bene.
Se qualcuno è interessato a fare lo stesso (o provare a compilare in modo incrociato altri moduli o binari del kernel per Synology), ecco la mia versione di Makefile . È stato testato con il sorgente OpenSSH-5.8p1 e funziona bene con i modelli che eseguono CPU Marvell Kirkwood mv6281/mv6282 (come DS212+). Ho usato un host con Ubuntu 12.10 x64.
In conclusione:cattiva pratica, codice terribile e un ottimo esempio di cosa non da fare. Capisco che a volte gli OEM debbano sviluppare personalizzazioni speciali, ma dovrebbero pensarci due volte prima di scavare troppo in profondità. Non solo questo si traduce in un codice non gestibile per loro, ma crea anche ogni sorta di problemi imprevisti lungo la strada. Per fortuna esiste GPL per mantenerli onesti e aperti.
Soluzione 2:
Per aggirare il problema, e poiché ho installato bash tramite ipkg e non posso essere sicuro che /opt sarà sempre disponibile (montato correttamente), ho semplicemente inserito quanto segue nel mio .profile
[ -x /opt/bin/bash ] && exec /opt/bin/bash
mentre /etc/passwd contiene /bin/ash come shell.