Solution 1 :
Pour référence future :après trop d'heures de recherche et de débogage de ce problème, j'ai finalement découvert la cause première.
La version OpenSSH utilisée par Synology est une version hautement personnalisée, qui ne le fait pas se comporter comme le code d'origine. Il comporte de nombreux hacks et personnalisations ad hoc - par exemple, une vérification supplémentaire avant d'accepter une connexion pour voir si le service SSH est activé dans l'interface Web, ou la suppression des caractères spéciaux (;, |, ') des commandes rsync, ou .. . attendez... éviter aux utilisateurs réguliers d'utiliser un shell différent de /bin/sh ou /bin/ash . Ouais, codé en dur dans le binaire.
Voici le morceau de code d'OpenSSH 5.8p1, tel que distribué par Synology sur leur code source (DSM4.1 - branche 2636), fichier 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 */
...
}
Comme vous pouvez l'imaginer, le IsAllowShell(pw)
était le coupable :
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;
}
Pas étonnant que je connaisse un comportement aussi étrange. Seuls les shells /bin/sh et /bin/ash seraient acceptés pour les utilisateurs autres que root ou administrateur . Et ceci quel que soit l'uid (j'avais testé aussi en faisant joeuser uid=0, et cela n'a pas fonctionné. Maintenant, il est évident pourquoi).
Une fois la cause identifiée, la solution était simple :supprimez simplement l'appel à IsAllowShell() . Il m'a fallu un certain temps pour obtenir la bonne configuration pour cross-compiler openssh et toutes ses dépendances, mais cela a bien fonctionné au final.
Si quelqu'un est intéressé à faire de même (ou à essayer de compiler d'autres modules du noyau ou des binaires pour Synology), voici ma version de Makefile . Il a été testé avec la source OpenSSH-5.8p1 et fonctionne bien avec les modèles exécutant le processeur Marvell Kirkwood mv6281/mv6282 (comme DS212+). J'ai utilisé un hôte exécutant Ubuntu 12.10 x64.
Conclusion :mauvaise pratique, code épouvantable et un excellent exemple de ce qui n'est pas faire. Je comprends que parfois les OEM doivent développer des personnalisations spéciales, mais ils devraient réfléchir à deux fois avant de creuser trop profondément. Non seulement cela se traduit par un code non maintenable pour eux, mais crée également toutes sortes de problèmes imprévus sur la route. Heureusement, GPL existe pour les garder honnêtes - et ouverts.
Solution 2 :
Pour contourner le problème, et comme j'ai installé bash via ipkg et que je ne peux pas être sûr que /opt sera toujours disponible (monté correctement), j'ai simplement mis ce qui suit dans mon .profile
[ -x /opt/bin/bash ] && exec /opt/bin/bash
tandis que /etc/passwd contient /bin/ash en tant que shell.