콘솔 사용자가 프로그램을 호출하는지 테스트하는 방법은 무엇입니까?

콘솔 사용자가 프로그램을 호출하는지 테스트하는 방법은 무엇입니까?

다음을 통해 HID 장치를 시뮬레이트하는 애플리케이션을 구축했습니다./dev/uhid리눅스에서. 내 지원서는 두 가지 프로그램으로 나뉩니다. 첫째, /dev/uhid장치를 열고 에뮬레이트하고 장치를 호출하는 프로그램에 메시지를 주고받는 매우 간단한 setuid 루트 바이너리입니다 . 둘째, 애플리케이션에는 실제로 모든 장치 로직이 포함되어 있으며 다른 바이너리를 사용하여 uhid_event메시지를 캡슐화하고 커널과 통신합니다.

콘솔에 액세스할 수 있는 사람은 누구나 하드웨어 USB 장치를 연결할 수 있지만 보안상의 이유로 setuid 프로그램이 콘솔이 아닌 사용자를 대신하여 실행되는 것을 거부하고 싶습니다.

내 질문: setuid 루트 응용 프로그램이 콘솔 사용자에 의해 호출되었는지 확인하고 그렇지 않은 경우 보석금을 내거나 처음에 콘솔 사용자에게 프로그램 실행을 제한하는 가장 간단하고 안정적인 방법은 무엇입니까?

답변1

콘솔이란 하드웨어 콘솔, 시스템 콘솔 또는 VT를 의미합니까? 후자를 의미한다면 정말 간단한 해결책은 STD*_FILENO가 각 tty와 연관되어 있는지 테스트하고 isatty()를 호출하여 VT와 연관되어 있는 스트림이 있는지 확인하는 것입니다. 그렇다면 PID가 프로세스 그룹 ID와 동일한지 확인하세요. 표준 스트림 중 하나가 tty와 연관되어 있고 현재 프로세스가 프로세스 그룹 리더인 경우 프로그램은 일종의 VT에서 사용자에 의해 실행될 가능성이 높습니다.

편집 1: 더 명확히 하기 위해 원래 질문은 가상 터미널이 아니라 다른 모든 것이 아닌 로컬/원격 로그인에 관한 것이었습니다. 위의 답변은 의미가 없습니다.

내가 아는 한, User Accounting Database UTMP/UTMPX API는 원격 로그인을 언급하는 유일한 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()파일 설명자와 연결된 터미널의 null로 끝나는 경로 이름을 저장해야 합니다 . 배열은 문자 길이이며 이름과 null 문자 종료를 위한 공간을 남겨 두어야 합니다. 단말기 이름의 최대 길이는 {TTY_NAME_MAX}이어야 합니다.fildesnamenamesize

0(또는 )을 통해 STDIN_FILENO이식 가능한 방식으로 프로세스 제어 터미널의 이름을 얻으십시오 .

내가 확인한 모든 Linux 인스턴스에서 텍스트 콘솔에 로그인한 사용자는 /dev/ttyNtty를 가지고 있습니다.

텍스트 콘솔 로그인에는 괜찮은 것 같습니다.

그래픽 로그인이 좀 어렵네요. Linux에서는 다음과 같은 의사 터미널 이름이 표시됩니다 /dev/pts/N. 이는 DISPLAY환경 변수가 제어 터미널임을 의미합니다. 첫 번째 근사치로, 또는 인 경우 :0프로세스 :0.0는 물리적 콘솔에 로그인한 누군가에 의해 실행되는 것이 거의 확실합니다. 환경 변수 XOpenDisplay()의 값을 사용하여 호출하여 DISPLAY결정할 수도 있지만 이것으로 충분할 수 있습니다 .

이는 물리적 콘솔에 로그인된 디스플레이에서 시작하는 :0대신 XVNC 또는 기타 원격 데스크톱 프로토콜을 사용하여 해당 사용자에게 디스플레이를 제공하도록 구성된 시스템에 액세스하는 사람을 잘못 식별합니다 . :10본 적이 없는 일이지만 이론적으로는 가능합니다.

이 문제를 처리할 필요가 없었다면 이제 프로세스가 Linux 물리적 콘솔에서 실행되고 있음을 확인한 것입니다.

이 상황을 처리해야 한다면 연결하려는 X 서버가 물리적 콘솔에서 실행되고 있는지 확인해야 합니다. 그런데, 이 작업을 수행하는 쉬운 방법은 모르겠지만 로컬 디스플레이인 경우 PID를 가져올 수 있어야 합니다 lsof /tmp/.X11-unix/XN(비록 구문 분석된 출력이 fuser더 쉬울 수 있지만) . 환경 변수 N의 디스플레이 번호는 어디에 있습니까 ? DISPLAYPID가 있으면 /proc/PID/fd/0X 서버의 제어 터미널을 읽기 위해 읽을 수 있으며, PID가 있으면 /dev/ttyN다시 사용자가 물리적 콘솔에 로그인하여 시스템에 물리적으로 액세스할 수 있음을 나타냅니다.

관련 정보