コンソールユーザーがプログラムを呼び出すかどうかをテストする方法は?

コンソールユーザーがプログラムを呼び出すかどうかをテストする方法は?

以下を通じて、HIDデバイスをシミュレートするアプリケーションを構築しました。/dev/uhidLinuxで。私の申し込みは2つのプログラムに分かれています。まず、/dev/uhidデバイスを開いてエミュレートし、デバイスを呼び出すプログラムにメッセージを送受信する非常に簡単なsetuidルートバイナリです。第二に、アプリケーションは実際にはすべてのデバイスロジックを含み、他のバイナリを使用してuhid_eventメッセージをカプセル化し、カーネルと通信します。

コンソールにアクセスできる人は誰でもハードウェアUSBデバイスを接続できますが、セキュリティ上の理由からsetuidプログラムがコンソールではなくユーザーに代わって実行されることを拒否したいと思います。

私の質問:setuid rootアプリケーションがコンソールユーザーによって呼び出されたことを確認し、そうでない場合は宝石を払うか、最初にコンソールユーザーにプログラムの実行を制限する最も簡単で信頼性の高い方法は何ですか?

答え1

コンソールとは、ハードウェアコンソール、システムコンソール、またはVTを意味しますか?後者を意味する場合、本当に簡単な解決策は、STD * _FILENOが各ttyに関連付けられているかどうかをテストし、isatty()を呼び出してVTに関連付けられているストリームがあるかどうかを確認することです。その場合は、PIDがプロセスグループIDと同じであることを確認してください。標準ストリームの1つが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で終わるパス名を格納する必要があります。配列は文字の長さであり、名前とヌル文字を終了するためのスペースを残す必要があります。端末名の最大長は{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ユーザーが物理コンソールにログインしてシステムに物理的にアクセスできることを示します。

関連情報