12

移植性の高いセキュリティコードを書いています。の一部のバージョンで見られるようなユーティリティ プログラムのセキュリティ上の欠陥を回避しようとしていますsudo:

sudo -k... を実行し、システム クロックを 01-01-1970 にリセットすることで、スーパー ユーザーになることができます。

これは、sudoアクセスがタイムアウトしたかどうかを判断するために絶対 (カレンダー) 時間に依存するために発生します。

私の考えは、CLOCK_MONOTONIC定義済みを使用することですtime.h.

POSIX標準から、

[CLOCK_MONOTONIC は] clock_settime() を介して値を設定できず、後方へのクロック ジャンプができないクロックとして定義されます。可能な最大クロック ジャンプは、実装によって定義されるものとします。

問題は、多くの (ほとんどの?) システムで、CLOCK_MONOTONIC再起動時にリセットされることです。プログラムが最後に実行されてからシステムが再起動されたかどうかを判断する、保証された POSIX 準拠の方法はありますか?

1 つの (悪い) 方法は、保存されたクロック値が現在のクロック値より大きいかどうかを確認することですが、これは問題をシフトするだけです。再起動時にリセットされるシステムでは、アクセスが許可CLOCK_MONOTONICされる短いウィンドウが存在する可能性があります。TIMEOUT

この問題を回避するために何が欠けていますか?

4

2 に答える 2

10

これは、POSIX共有メモリオブジェクトを使用して行うのは簡単だと思います:

POSIX 共有メモリ オブジェクトにはカーネル永続性があります。共有メモリ オブジェクトは、システムがシャットダウンされるか、すべてのプロセスがオブジェクトのマップを解除し、shm_unlink で削除されるまで存在します。

プログラムが起動するたびに、shm_open一貫した名前を持つ新しいオブジェクトを作成し、所有者を に設定できrootます。オブジェクトに特定の値を含める必要はありません。POSIX では、手動で破棄しない限り、すべての共有メモリ オブジェクトが再起動するまで存続する必要があります (これは、その所有者または作成者のみが実行できます...この場合は root ユーザーです)。

プログラムを起動するときはいつでも、最初にそのような共有メモリ オブジェクトが存在し、所有者として root を持っているかどうかをチェックします。そのようなオブジェクトを作成できるのは root だけであり、それを破棄できるのは root または再起動だけなので、前回の再起動以降にプログラムが起動されたかどうかを確実に知ることができます。ルート ユーザーshm_unlinkがオブジェクトを手動で呼び出すことだけを回避できます。 .

以下に、必要なことを正確に実行するテストと設定の関数を書きました。そして、所有権の設定/検出を除いて機能します。何らかの不明な理由shmctlで、システムで両方の呼び出しが失敗し、「無効な引数」と言われています。のmanページにshmctlは、EINVALエラーが無効なメモリ オブジェクト識別子または無効なコマンドのいずれかを示していると記載されています。ただし、コマンドIPC_SETIPC_STATコマンドは確かに有効であり、プログラムの出力を見て、毎回作成および/または開かれている有効なオブジェクト識別子を確認できます。

#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>

int rebooted_test_and_set() {
    int err;
    int rebooted;
    struct shmid_ds shmst;
    // create object if nonexistent, returning failure if already exists
    int shmid = shm_open("/bootcheck", O_CREAT | O_EXCL);
    if (shmid != -1) {
        fprintf(stderr, "bootcheck object did not exist, so created: %d\n", shmid);
        // object did not exist, so system has been rebooted
        rebooted = 1;
        // set owner to root, and no permissions for anyone
        shmst.shm_perm.uid = 0;
        shmst.shm_perm.gid = 0;
        shmst.shm_perm.mode = 0;
        if ((err = shmctl(shmid, IPC_SET, &shmst)) == -1) {
            perror("shmctl: shmctl failed to set owner and permissions for bootcheck object");
            exit(1);
        }
    } else {
        // object already exists, so reopen with read access and verify that the owner is root
        shmid = shm_open("/bootcheck", O_RDONLY);
        if (shmid == -1) {
            perror("shm_open: failed, perhaps due to insufficient privileges");
            exit(1);
        }
        fprintf(stderr, "bootcheck object (%d) exists, so checking ownership\n", shmid);
        if ((err = shmctl(shmid, IPC_STAT, &shmst)) == -1) {
            perror("shmctl: shmctl failed");
            exit(1);
        }
        if (shmst.shm_perm.uid == 0) {
            // yes, the bootcheck owner is root,
            // so we are confident the system has NOT been rebooted since last launch
            rebooted = 0;
        } else {
            // uh oh, looks like someone created the object illegitimately.
            // since that is only possible if the root-owned object did not exist, 
            // therefore we know that it never did exist since the last reboot
            rebooted = 1;
        }
    }
    return rebooted;
}

// for debugging purposes ONLY, so I don't have to keep rebooting to clear the object:
void rebooted_clear() {
    if (shm_unlink("/bootcheck") == -1) {
        perror("shm_unlink: failed, probably due to insufficient privileges or object nonexistent");
        exit(1);
    }
}

int main() {
    int rebooted = rebooted_test_and_set();
    printf("rebooted since last launch: %d\n", rebooted);
    return 0;
}

誰かが手がかりを持っているなら、私は困惑しています。POSIX 共有メモリに関する情報と例については、こちらを参照してください。

于 2013-12-12T00:11:59.100 に答える
1

このpython ライブラリでは、utmp の最後の BOOT_TIME エントリを探します。技術的には、POSIX にあるのは utmpx (ファイル形式) とそれにアクセスするための libc 関数です。これは、POSIX 内にとどまることができるのと同じくらい良いと思います。

于 2013-12-11T22:01:16.613 に答える