我正在尝试将 bash 安装为在嵌入式设备 (Synology DS212+ NAS) 上运行的 ARM Linux 上的默认 shell。但确实出了点问题,我不知道是什么问题。
症状:
1)Root 有 /bin/bash 作为默认 shell,并且可以通过 SSH 正常登录:
$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
$ ssh root@NAS
root@NAS's password:
Last login: Sun Dec 16 14:06:56 2012 from desktop
#
2) joeuser 将 /bin/bash 作为默认 shell,并在尝试通过 SSH 登录时收到“权限被拒绝”:
$ grep joeuser /etc/passwd
joeuser:x:1029:100:Joe User:/home/joeuser:/bin/bash
$ ssh joeuser@localhost
joeuser@NAS's password:
Last login: Sun Dec 16 14:07:22 2012 from desktop
Permission denied, please try again.
Connection to localhost closed.
3)将 joeuser 的 shell 改回 /bin/sh:
$ grep joeuser /etc/passwd
joeuser:x:1029:100:Joe User:/home/joeuser:/bin/sh
$ ssh joeuser@localhost
Last login: Sun Dec 16 15:50:52 2012 from localhost
$
更奇怪的是,我可以使用/bin/bash
串行控制台以 joeuser 身份登录 (!)。此外,su - joeuser
以 root 身份运行正常,因此 bash 二进制文件本身运行正常。
无奈之下,我在 /etc/passwd 上将 joeuser 的 uid 改为 0,但也没有用,所以这似乎与权限无关。
似乎 bash 正在执行 sshd 不喜欢的额外检查,并阻止非 root 用户的连接。也许是某种健全性检查 - 或终端仿真 - 触发了 SIGCHLD,但仅在通过 ssh 调用时才会触发。
我已经检查了 sshd_config 上的每一个项目,并将 SSHD 置于调试模式,但没有发现任何奇怪的东西。这是我的/etc/ssh/sshd_config
:
LogLevel DEBUG
LoginGraceTime 2m
PermitRootLogin yes
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile %h/.ssh/authorized_keys
ChallengeResponseAuthentication no
UsePAM yes
AllowTcpForwarding no
ChrootDirectory none
Subsystem sftp internal-sftp -f DAEMON -u 000
下面是 的输出/usr/syno/sbin/sshd -d
,显示 joeuser 尝试使用 /bin/bash 作为 shell 登录失败:
debug1: Config token is loglevel
debug1: Config token is logingracetime
debug1: Config token is permitrootlogin
debug1: Config token is rsaauthentication
debug1: Config token is pubkeyauthentication
debug1: Config token is authorizedkeysfile
debug1: Config token is challengeresponseauthentication
debug1: Config token is usepam
debug1: Config token is allowtcpforwarding
debug1: Config token is chrootdirectory
debug1: Config token is subsystem
debug1: HPN Buffer Size: 87380
debug1: sshd version OpenSSH_5.8p1-hpn13v11
debug1: read PEM private key done: type RSA
debug1: private host key: #0 type 1 RSA
debug1: read PEM private key done: type DSA
debug1: private host key: #1 type 2 DSA
debug1: read PEM private key done: type ECDSA
debug1: private host key: #2 type 3 ECDSA
debug1: rexec_argv[0]='/usr/syno/sbin/sshd'
debug1: rexec_argv[1]='-d'
Set /proc/self/oom_adj from 0 to -17
debug1: Bind to port 22 on ::.
debug1: Server TCP RWIN socket size: 87380
debug1: HPN Buffer Size: 87380
Server listening on :: port 22.
debug1: Bind to port 22 on 0.0.0.0.
debug1: Server TCP RWIN socket size: 87380
debug1: HPN Buffer Size: 87380
Server listening on 0.0.0.0 port 22.
debug1: Server will not fork when running in debugging mode.
debug1: rexec start in 6 out 6 newsock 6 pipe -1 sock 9
debug1: inetd sockets after dupping: 4, 4
Connection from 127.0.0.1 port 52212
debug1: HPN Disabled: 0, HPN Buffer Size: 87380
debug1: Client protocol version 2.0; client software version OpenSSH_5.8p1-hpn13v11
SSH: Server;Ltype: Version;Remote: 127.0.0.1-52212;Protocol: 2.0;Client: OpenSSH_5.8p1-hpn13v11
debug1: match: OpenSSH_5.8p1-hpn13v11 pat OpenSSH*
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_5.8p1-hpn13v11
debug1: permanently_set_uid: 1024/100
debug1: MYFLAG IS 1
debug1: list_hostkey_types: ssh-rsa,ssh-dss,ecdsa-sha2-nistp256
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: AUTH STATE IS 0
debug1: REQUESTED ENC.NAME is 'aes128-ctr'
debug1: kex: client->server aes128-ctr hmac-md5 none
SSH: Server;Ltype: Kex;Remote: 127.0.0.1-52212;Enc: aes128-ctr;MAC: hmac-md5;Comp: none
debug1: REQUESTED ENC.NAME is 'aes128-ctr'
debug1: kex: server->client aes128-ctr hmac-md5 none
debug1: expecting SSH2_MSG_KEX_ECDH_INIT
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: KEX done
debug1: userauth-request for user joeuser service ssh-connection method none
SSH: Server;Ltype: Authname;Remote: 127.0.0.1-52212;Name: joeuser
debug1: attempt 0 failures 0
debug1: Config token is loglevel
debug1: Config token is logingracetime
debug1: Config token is permitrootlogin
debug1: Config token is rsaauthentication
debug1: Config token is pubkeyauthentication
debug1: Config token is authorizedkeysfile
debug1: Config token is challengeresponseauthentication
debug1: Config token is usepam
debug1: Config token is allowtcpforwarding
debug1: Config token is chrootdirectory
debug1: Config token is subsystem
debug1: PAM: initializing for "joeuser"
debug1: PAM: setting PAM_RHOST to "localhost"
debug1: PAM: setting PAM_TTY to "ssh"
debug1: userauth-request for user joeuser service ssh-connection method password
debug1: attempt 1 failures 0
debug1: do_pam_account: called
Accepted password for joeuser from 127.0.0.1 port 52212 ssh2
debug1: monitor_child_preauth: joeuser has been authenticated by privileged process
debug1: PAM: establishing credentials
User child is on pid 9129
debug1: Entering interactive session for SSH2.
debug1: server_init_dispatch_20
debug1: server_input_channel_open: ctype session rchan 0 win 65536 max 16384
debug1: input_session_request
debug1: channel 0: new [server-session]
debug1: session_new: session 0
debug1: session_open: channel 0
debug1: session_open: session 0: link with channel 0
debug1: server_input_channel_open: confirm session
debug1: server_input_global_request: rtype [email protected] want_reply 0
debug1: server_input_channel_req: channel 0 request pty-req reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req pty-req
debug1: Allocating pty.
debug1: session_new: session 0
debug1: session_pty_req: session 0 alloc /dev/pts/1
debug1: server_input_channel_req: channel 0 request shell reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req shell
debug1: Setting controlling tty using TIOCSCTTY.
debug1: Received SIGCHLD.
debug1: session_by_pid: pid 9130
debug1: session_exit_message: session 0 channel 0 pid 9130
debug1: session_exit_message: release channel 0
debug1: session_by_tty: session 0 tty /dev/pts/1
debug1: session_pty_cleanup: session 0 release /dev/pts/1
Received disconnect from 127.0.0.1: 11: disconnected by user
debug1: do_cleanup
debug1: do_cleanup
debug1: PAM: cleanup
debug1: PAM: closing session
debug1: PAM: deleting credentials
重击:
# bash --version
GNU bash, version 3.2.49(1)-release (arm-none-linux-gnueabi)
Copyright (C) 2007 Free Software Foundation, Inc.
bash 二进制文件是从源代码交叉编译的。我还尝试使用来自Optware 分销,但遇到了完全相同的问题。我使用 检查了缺少的共享库objdump -x
,但它们都在那里。
任何想法可能是什么原因造成的“权限被拒绝,请重试。“?我几乎要深入研究 bash 源代码了,但我又不想花几个小时去追逐一些可能很愚蠢的事情。
编辑:添加有关 bash 和系统的更多信息
$ ls -la /bin/bash
-rwxr-xr-x 1 root root 724676 Dec 15 23:57 /bin/bash
$ file /bin/bash
/bin/bash: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.14, stripped
$ uname -a
Linux NAS 2.6.32.12 #2661 Mon Nov 12 23:10:15 CST 2012 armv5tel GNU/Linux synology_88f6282_212+
$ grep bash /etc/shells
/bin/bash
/bin/bash2
答案1
供将来参考:经过大量时间研究和调试这个问题后,我终于发现了根本原因。
Synology 使用的 OpenSSH 版本是高度定制的版本,它不是行为与原始代码类似。它有很多技巧和临时定制 - 例如,在接受登录之前进行额外检查以查看 Web 界面中是否启用了 SSH 服务,或从 rsync 命令中删除特殊字符(;、|、'),或者...等待它...避免普通用户使用除 /bin/sh 或 /bin/ash 之外的 shell。是的,在二进制文件中进行了硬编码。
以下是 Synology 在其源代码中发布的 OpenSSH 5.8p1 中的一段代码(DSM4.1 - 分支 2636), 文件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 */
...
}
你可以想象,IsAllowShell(pw)
罪魁祸首是:
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;
}
难怪我遇到了如此奇怪的行为。只有 shell /bin/sh 和 /bin/ash 会被接受,但用户不是根或者行政。无论 uid 如何(我也测试过制作乔伊瑟uid=0,并且它不起作用。现在原因很明显了)。
一旦确定了原因,修复就很容易了:只需删除对允许Shell()。我花了一段时间才获得正确的配置来交叉编译 openssh 及其所有依赖项,但最终效果很好。
如果有人有兴趣做同样的事情(或尝试为 Synology 交叉编译其他内核模块或二进制文件),这是我的版本Makefile. 测试用OpenSSH-5.8p1 源,并且与运行 Marvell Kirkwood mv6281/mv6282 CPU 的型号(如 DS212+)配合良好。我使用了运行 Ubuntu 12.10 x64 的主机。
底线:糟糕的实践,糟糕的代码,以及一个很好的例子不是要做。我理解 OEM 有时候需要开发特殊定制,但他们应该三思而后行,不要太深入。这不仅会导致代码无法维护,还会在日后产生各种无法预见的问题。幸好 GPL 的存在让他们保持诚实和开放。
答案2
为了解决这个问题,由于我通过 ipkg 安装了 bash,并且不能确定 /opt 是否始终可用(是否正确安装),所以我只需将以下内容放入我的 .profile 中
[ -x /opt/bin/bash ] && exec /opt/bin/bash
而 /etc/passwd 包含 /bin/ash 作为 shell。
答案3
让我们看看。它被隔离到单个 shell,而且您正在查看 sshd 调试输出,因此它不是 ~joeuser/.ssh 的全局可写权限问题。这是大多数人都遇到的问题。
您是否尝试过创建一个额外的普通用户(即非 joeuser)以确保它遇到同样的问题?这样可以将问题隔离到用户配置与系统范围配置之间。
如果这是系统范围内的问题,那么接下来我要查看的是每个人都使用的共享配置文件,例如 /etc/profile。如果用户名是 root,则可能有一个条件块不会触发。(不是有效用户 ID,因为您已经对此进行了测试)
如果还没有,请检查 dmesg 中的分段错误报告,以防发生更奇怪的事情。
答案4
他们修改的 openssh 版本寻找/bin/sh
shell ?
那么简单的解决方案是:
ln -fs /bin/bash /bin/sh