2013-08-30 44 views
12

Tôi đang viết một số mã bảo mật di động cao. Tôi đang cố gắng để tránh lỗ hổng bảo mật trong một chương trình tiện ích như this one tìm thấy trong một số phiên bản của sudo:Cách tương thích POSIX để biết liệu hệ thống đã khởi động lại chưa?

... nó có thể trở thành siêu người dùng bằng cách chạy sudo -k và sau đó thiết lập lại đồng hồ hệ thống đến 01 -01-1970.

Điều này xảy ra vì sudo dựa vào thời gian tuyệt đối (hay còn gọi là lịch) để xác định xem quyền truy cập đã hết thời gian hay chưa.

Ý tưởng của tôi là sử dụng CLOCK_MONOTONIC quy định tại time.h.

Từ POSIX standard,

[CLOCK_MONOTONIC là] định nghĩa là một chiếc đồng hồ có giá trị không thể được thiết lập thông qua clock_settime() và trong đó không thể có đồng hồ ngược nhảy. Việc nhảy đồng hồ tối đa có thể được thực hiện xác định.

Sự cố, trên nhiều hệ thống (nhiều nhất), CLOCK_MONOTONIC đặt lại khi khởi động lại. Có bất kỳ nào được đảm bảo cách tuân thủ POSIX để xác định xem hệ thống đã khởi động lại hay chưa kể từ khi chương trình chạy lần cuối?

Cách một (xấu) là kiểm tra xem giá trị đồng hồ đã lưu có lớn hơn giá trị đồng hồ hiện tại hay không, tuy nhiên điều này chỉ làm thay đổi vấn đề. Trên các hệ thống trong đó CLOCK_MONOTONIC đặt lại khi khởi động lại, có thể có một cửa sổ ngắn có chiều dài TIMEOUT nơi quyền truy cập sẽ được cho phép.

Tôi thiếu điều gì sẽ tránh được sự cố này?

+1

Đối với những người không cần đảm bảo tính tương thích POSIX: '/ proc/sys/kernel/random/boot_id' có lẽ là những gì bạn đang tìm kiếm. @BenBurns: Bạn có thể tạo tệp ở một số vị trí "được bảo đảm tạm thời" nếu có, nếu tệp không có, hệ thống đã được khởi động lại. Nếu không, nếu bạn có một quá trình chạy vĩnh viễn, giả sử hệ thống không được khởi động lại trong khi quá trình của bạn đang chạy và giả sử hệ thống có thể đã được khởi động lại nếu quá trình của bạn mới bắt đầu. –

+0

Bạn không thể sử dụng kết hợp thời gian hệ thống và 'CLOCK_MONOTONIC' để phát hiện lệch đồng hồ và/hoặc khởi động lại hệ thống và buộc xác thực lại trong một trong các trường hợp đó? – mpontillo

+0

Một ý tưởng khác có thể là chia rẽ một quá trình lâu dài (tương tự như cách tác nhân ssh-agent) và làm IPC với nó. – mpontillo

Trả lời

10

Dường như với tôi đây là đơn giản để làm sử dụng một đối tượng POSIX shared memory:

POSIX chia sẻ đối tượng bộ nhớ có kiên trì hạt nhân: a chia sẻ bộ nhớ đối tượng sẽ tồn tại cho đến khi hệ thống được tắt, hoặc cho đến khi tất cả quá trình có unmapped đối tượng và nó đã bị xóa với shm_unlink

Bất cứ khi nào chương trình của bạn khởi động nó có thể shm_open một đối tượng mới với một số tên phù hợp và thiết lập các chủ sở hữu đến root. Đối tượng không cần chứa bất kỳ giá trị cụ thể nào. POSIX yêu cầu tất cả các đối tượng bộ nhớ chia sẻ tồn tại cho đến khi khởi động lại trừ khi bị hủy thủ công (chỉ chủ sở hữu hoặc người tạo của nó mới có thể thực hiện ... trong trường hợp này là người dùng root).

Bất cứ khi nào chương trình của bạn khởi chạy chương trình trước tiên sẽ kiểm tra xem đối tượng bộ nhớ dùng chung đã tồn tại có gốc với tư cách chủ sở hữu chưa. Vì chỉ root mới có thể tạo ra một đối tượng như vậy, và chỉ root hoặc khởi động lại mới có thể phá hủy nó, bạn có thể biết chắc chắn chương trình của bạn đã được khởi động từ lần khởi động gần đây hay chưa. bằng tay.

Tôi đã viết một hàm thử và đặt dưới đây nên thực hiện chính xác những gì bạn cần. Và nó hoạt động ngoại trừ thiết lập quyền sở hữu/phát hiện: vì một số lý do không rõ, cả hai cuộc gọi đến shmctl đều không thành công trên hệ thống của tôi, nói "đối số không hợp lệ". Trang man cho shmctl cho biết lỗi EINVAL cho biết số nhận dạng đối tượng bộ nhớ không hợp lệ hoặc lệnh không hợp lệ. Nhưng các lệnh IPC_SETIPC_STAT chắc chắn là hợp lệ và bạn có thể xem đầu ra của chương trình để xem số nhận dạng đối tượng hợp lệ đang được tạo và/hoặc mở mỗi lần.

#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; 
} 

Nếu có bất kỳ manh mối nào, tôi bị bối rối. Một số thông tin và ví dụ về bộ nhớ chia sẻ POSIX here.

+0

Xin lỗi, tôi vừa nhận ra rằng bạn có thể không tin tưởng vào gốc, trong trường hợp đó toàn bộ ý tưởng của tôi là vô dụng đối với bạn. Lấy làm tiếc. – jrodatus

+3

Không tin tưởng root sẽ làm cho bất kỳ nỗ lực bảo mật trên hệ thống đó là vô nghĩa, vì gốc có thể, theo định nghĩa, làm bất cứ điều gì. Bạn đã cung cấp một câu trả lời tuyệt vời! –

+0

@jrodatus, đã lâu lắm rồi, nhưng tôi mới nhận thấy bình luận của bạn. Điều này rõ ràng chỉ giúp hoàn thành chiến lược 'CLOCK_MONOTONIC' để tránh lỗ hổng của các quá trình tồn tại trong thời gian ngắn lưu trữ và kiểm tra thời gian hệ thống cho thời gian chờ mà người dùng * không phải root * có thể thay đổi thời gian của hệ thống. –

1

Trong this python library, họ tìm mục nhập BOOT_TIME cuối cùng bằng utmp. Về mặt kỹ thuật như những gì có trong POSIX là utmpx (định dạng tệp) và các hàm libc để truy cập nó. Tôi nghĩ rằng điều này là tốt như bạn có thể nhận được ở trong POSIX.

+1

Bạn chắc chắn về điều đó? Tôi không có chuyên gia POSIX, nhưng theo mục nhập trên utmp của Wikipedia, "Các tệp utmp, wtmp và btmp không bao giờ là một phần của bất kỳ chuẩn Unix chính thức nào, chẳng hạn như Đặc tả UNIX đơn" – jrodatus

+0

utmpx là định dạng tệp POSIX và tên tệp khác nhau giữa các nền tảng Ngoài ra còn có các chức năng của POSIX libc để truy cập vào tệp tin trừu tượng ra khỏi tên tệp thực của nó. – abasterfield

+0

giải pháp cập nhật để làm rõ những gì trong POSIX – abasterfield

Các vấn đề liên quan