如何测试程序是否由控制台用户调用?

如何测试程序是否由控制台用户调用?

我已经构建了一个通过以下方式模拟 HID 设备的应用程序/dev/uhid在Linux上。我的应用程序分为两个程序。首先,一个非常简单的 setuid root 二进制文件,它/dev/uhid仅打开并模拟一个设备,将消息来回传递给调用它的程序。其次,一个应用程序实际上包含所有设备逻辑,并使用其他二进制文件来封装uhid_event消息并与内核通信。

任何具有控制台访问权限的人都可以插入硬件 USB 设备,但为了安全起见,我希望 setuid 程序拒绝代表非控制台用户运行。

我的问题:setuid root 应用程序检查它是否由控制台用户调用并在没有调用时进行保释,或者首先将程序的执行限制为控制台用户,最简单、最可靠的方法是什么?

答案1

您所说的控制台是指硬件控制台、系统控制台还是任何 VT?如果您的意思是后者,一个真正简单的解决方案是测试 STD*_FILENO 是否与每个 tty 关联,并调用 isatty() 来查看是否有任何流与 VT 关联。如果是,则检查 PID 是否等于进程组 ID。如果其中一个标准流与 tty 关联,并且当前进程是进程组领导者,则该程序很可能是由用户在某种 VT 上运行的。

编辑1:进一步澄清,最初的问题是关于本地/远程登录而不是虚拟终端与其他所有问题,上面的答案是没有意义的。

据我所知,用户会计数据库 UTMP/UTMPX API 是唯一提到远程登录的数据库,因此这可能是最好的解决方案。只需搜索用户记帐数据库 ut_host 字段即可查看有效的 IP 地址是否与用户登录相关联。

答案2

所以我不知道这是否安全,但从表面上看它似乎给了我我想要的东西。必须与 联系起来-lsystemd。希望有人对安全性发表评论或发布更好的答案......

#include <cstring>
#include <iostream>

#include <stdlib.h>
#include <unistd.h>
#include <systemd/sd-login.h>

int
is_remote()
{
  char *s = NULL;
  int n = sd_pid_get_session(getpid(), &s);
  if (n  < 0) {
    std::cerr << "sd_pid_get_session: " << std::strerror(-n) << std::endl;
    return -1;
  }
  n = sd_session_is_remote(s);
  free(s);
  if (n < 0) {
    std::cerr << "sd_pid_get_session: " << std::strerror(-n) << std::endl;
    return -1;
  }
  return n;
}

int
main(int argc, char **argv)
{
  if (is_remote()) {
    std::cerr << "remote access not allowed" << std::endl;
    return 1;
  }
  // Do actual program ...
  return 0;
}

答案3

如果您使用 C 或 C++,并且不需要 systemd 依赖项,则可以使用 POSIX 标准ttyname()或者ttyname_r()获取进程的控制终端:

概要

#include <unistd.h>

char *ttyname(int fildes);
int ttyname_r(int fildes, char *name, size_t namesize);

描述

ttyname()函数应返回一个指向字符串的指针,该字符串包含与文件描述符关联的终端的以空结尾的路径名fildes。应用程序不得修改返回的字符串。返回的指针可能会失效,或者字符串内容可能会被后续调用覆盖ttyname()。如果调用线程终止,返回的指针和字符串内容也可能无效。

ttyname()函数不必是线程安全的。

ttyname_r()函数应将与文件描述符关联的终端的以空结尾的路径名存储fildes在 引用的字符数组中name。该数组的namesize长度为字符,并且应该为名称和终止空字符留出空间。终端名称的最大长度应为{TTY_NAME_MAX}。

只需通过0(或STDIN_FILENO) 以可移植的方式获取进程控制终端的名称。

在我检查过的每个 Linux 实例上,登录到文本控制台的用户都有一个/dev/ttyNtty。

这对于文本控制台登录来说似乎是确定的。

图形登录有点困难。在 Linux 上,您将获得一个伪终端名称,例如/dev/pts/N.这意味着您的DISPLAY环境变量是您的控制终端。作为第一个近似值,如果是:0:0.0,则该进程几乎肯定是由登录到物理控制台的某人运行的。这对您来说可能就足够了,尽管您可以使用环境变量XOpenDisplay()中的值进行调用DISPLAY来确定。

这将错误地识别任何使用 XVNC 或其他远程桌面协议来访问配置为向这些用户提供:0显示的系统的人,而不是从:10登录到物理控制台的显示开始。这是我从未见过的事情,但理论上是可能的。

如果您不必处理这个问题,那么您现在已经确定您的进程正在 Linux 物理控制台上运行。

如果您确实必须处理这种情况,则必须确定您正在连接的 X 服务器是否正在物理控制台上运行。顺便说一句,我不知道有什么简单的方法可以做到这一点,但是如果它是本地显示器,您应该能够从中获取 PID lsof /tmp/.X11-unix/XN(尽管解析 的输出fuser可能更容易),环境变量N中的显示编号在哪里DISPLAY。一旦获得 PID,您就可以读取/proc/PID/fd/0以获取 X 服务器的控制终端,如果是/dev/ttyN,则再次表明用户已登录到物理控制台,可以对系统进行物理访问。

相关内容