Solusi 1:
Untuk referensi di masa mendatang:setelah berjam-jam meneliti dan men-debug masalah ini, saya akhirnya menemukan akar masalahnya.
Versi OpenSSH yang digunakan oleh Synology adalah versi yang sangat disesuaikan, yang tidak berperilaku seperti kode asli. Ini memiliki banyak peretasan dan penyesuaian ad-hoc - misalnya, pemeriksaan tambahan sebelum menerima login untuk melihat apakah layanan SSH diaktifkan dalam antarmuka web, atau menghapus karakter khusus (;, |, ') dari perintah rsync, atau.. . tunggu dulu... menghindari pengguna biasa menggunakan shell yang berbeda dari /bin/sh atau /bin/ash . Ya, kode keras dalam biner.
Berikut potongan kode dari OpenSSH 5.8p1, seperti yang didistribusikan oleh Synology pada kode sumbernya (DSM4.1 - cabang 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 */
...
}
Seperti yang dapat Anda bayangkan, IsAllowShell(pw)
adalah pelakunya:
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;
}
Tidak heran mengapa saya mengalami perilaku aneh seperti itu. Hanya shell /bin/sh dan /bin/ash yang akan diterima untuk pengguna selain root atau admin . Dan ini terlepas dari uidnya (saya telah menguji juga membuat joeuser uid =0, dan itu tidak berhasil. Sekarang sudah jelas mengapa).
Setelah mengidentifikasi penyebabnya, perbaikannya mudah:cukup hapus panggilan ke IsAllowShell() . Butuh beberapa saat bagi saya untuk mendapatkan konfigurasi yang tepat untuk mengkompilasi silang openssh dan semua dependensinya, tetapi pada akhirnya berhasil dengan baik.
Jika ada yang tertarik untuk melakukan hal yang sama (atau mencoba mengkompilasi silang modul kernel atau binari lain untuk Synology), inilah versi Makefile saya . Itu diuji dengan sumber OpenSSH-5.8p1, dan bekerja dengan baik dengan model yang menjalankan CPU Marvell Kirkwood mv6281/mv6282 (seperti DS212+). Saya menggunakan host yang menjalankan Ubuntu 12.10 x64.
Intinya:praktik buruk, kode buruk, dan contoh bagus tentang apa yang tidak melakukan. Saya mengerti terkadang OEM perlu mengembangkan penyesuaian khusus, tetapi mereka harus berpikir dua kali sebelum menggali terlalu dalam. Tidak hanya ini menghasilkan kode yang tidak dapat dipertahankan untuk mereka, tetapi juga menciptakan segala macam masalah tak terduga di kemudian hari. Syukurlah GPL hadir untuk menjaga mereka tetap jujur - dan terbuka.
Solusi 2:
Untuk menghindari masalah, dan karena saya menginstal bash melalui ipkg dan saya tidak yakin /opt akan selalu tersedia (dipasang dengan benar), saya cukup meletakkan yang berikut di .profile
saya[ -x /opt/bin/bash ] && exec /opt/bin/bash
sementara /etc/passwd berisi /bin/ash sebagai shell.