把密码短语写入一个处于等待状态的cryptsetup进程
在基于BusyBox的 initramfs(Arch Linux mkinitcpio),我有 cryptsetup 等待来自控制台输入密码
ps | grep cryptsetup
200 root 11752 S cryptsetup open --type luks /dev/vda2 root
它处于 S 模式(可中断睡眠),如果可能的话,我不想在我的职责范围之外修改这个钩子。
由于我也希望能够通过网络解密根设备,我设法为initramfs提供了一个最小的DHCP环境,并通过这个钩子启动了一个Dropbear服务器:
run_hook ()
{
[ -d /dev/pts ] || mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo "Starting dropbear"
/usr/bin/dropbear -s -j -k -p 2222
}
到目前为止,一切还算顺利。我现在苦恼的是如何把口令传给等待中的200号进程,以便解锁分区并继续引导。
我做了一些基于向该进程的标准输入写入的简单尝试
printf "password\n" > /proc/200/fd/0
# EOL variant
cat > /proc/200/fd/0 << EOF
password
EOF
但失败了(与 /dev/console /proc/200/fd/0 指向的情况相同):明文被写入,但解密没有被触发。
~ # ls -la /proc/200/fd
dr-x------ 2 root 0 7 Mar 11 16:01 .
dr-xr-xr-x 9 root 0 0 Mar 11 15:49 ..
lrwx------ 1 root 0 64 Mar 11 16:05 0 -> /dev/console
lrwx------ 1 root 0 64 Mar 11 16:05 1 -> /dev/console
由于预期输入是一个不应被打印的口令,且/或因为进程处于 S 模式,我猜想 cryptsetup 要么监听另一台设备,要么某个事件应触发将输入转发到它的STDIN。我尝试了 /dev/tty0,一些 /dev/tty*,以及 /dev/pts 的条目。这个 interactive_pass 部分暗示 /dev/tty0
/* Read and write to /dev/tty if available */
infd = open("/dev/tty", O_RDWR);
if (infd == -1) {
infd = STDIN_FILENO;
outfd = STDERR_FILENO;
} else {
outfd = infd;
close_fd = true;
}
作为一个合适的候选者,/proc/200/stat 也是如此,但因为我在initramfs中没有 strace 或任何类似工具,所以我不确定这个函数可以使用哪一个tty。
对名义情况下的工作原理进行解释/澄清将对我有很大帮助。当 cryptsetup open --type luks /dev/vda2 触发时,我理解进程处于睡眠模式,等待一个回答。当我输入口令并按回车时,这些字符会通过哪条路径传达到等待中的 interactive_pass?是不是应该先把它从睡眠状态唤醒?
解决方案
我最终利用了Linux内核仍然以 TIOCSTI 标志编译这一事实。
inject_to_tty.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc < 2) return 1;
if (strlen(argv[1]) > 32) return 1;
int fd = open("/dev/tty0", O_RDWR);
if (fd == -1) {
return 1;
}
for (char *s = argv[1]; *s; s++)
ioctl(fd, TIOCSTI, s);
char nl = '\n';
ioctl(fd, TIOCSTI, &nl);
close(fd);
return 0;
}
使用我自定义的 /etc/initcpio/install/dropbear 将其编译并加载到initramfs,且我在dropbear会话中启动的解锁脚本只需要
#!/bin/sh
echo "Please provide the password to unlock the system partition:"
read -s PASSWORD
/usr/bin/inject_to_tty $PASSWORD
这样,我就不会修改任何现存的钩子。其他设置步骤(允许shell、在initramfs中创建 root 用户等)在这里有详细说明:这里