12

我正在编写一些高度便携的安全代码。我试图避免实用程序中的安全漏洞,例如在某些版本中发现的这个sudo

...可以通过运行sudo -k然后将系统时钟重置为01-01-1970来成为超级用户。

发生这种情况是因为sudo依赖绝对(又名日历)时间来确定访问是否超时。

我的想法是使用CLOCK_MONOTONIC定义在time.h.

POSIX 标准

[CLOCK_MONOTONIC is] 定义为一个时钟,其值不能通过clock_settime() 设置并且不能向后时钟跳转。最大可能的时钟跳变应由实现定义。

问题是,在许多(大多数?)系统CLOCK_MONOTONIC上,重新启动时会重置。是否有任何保证POSIX 兼容的方法来确定自上次运行程序以来系统是否已重新启动?

一种(不好的)方法是检查存储的时钟值是否大于当前时钟值,但这只是转移了问题。在重新启动时重置的系统上,可能会有一个允许访问CLOCK_MONOTONIC的短窗口。TIMEOUT

我错过了什么可以避免这个问题?

4

2 回答 2

10

在我看来,使用 POSIX共享内存对象很简单:

POSIX 共享内存对象具有内核持久性:共享内存对象将一直存在,直到系统关闭,或者直到所有进程都取消映射该对象并使用 shm_unlink 将其删除

每当您的程序启动时,它都可以shm_open创建一个具有一致名称的新对象并将所有者设置为root. 该对象不需要包含任何特定的值。POSIX 要求所有共享内存对象在重新启动之前一直存在,除非手动销毁(只有其所有者或创建者可以这样做......在这种情况下是 root 用户)。

每当您的程序启动时,它首先检查是否已经存在这样的共享内存对象,并以 root 作为所有者。由于只有 root 可以创建这样的对象,并且只有 root 或重新启动才能破坏它,因此您可以确定自上次重新启动以来您的程序是否已启动,唯一可能的规避是 root 用户shm_unlink手动调用该对象.

我在下面写了一个测试和设置函数,它应该完全符合你的需要。除了所有权设置/检测之外,它可以工作:由于某种未知的原因shmctl,我的系统上的两个调用都失败了,说“无效参数”。该man页面shmctl表示该EINVAL错误指示无效的内存对象标识符或无效的命令。但是IPC_SETandIPC_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 回答