Điều gì sẽ là cách tốt nhất để giải quyết vấn đề glibc này?


26

Tôi quản lý một hộp Gentoo Hardened sử dụng các khả năng của tệp để loại bỏ hầu hết nhu cầu về các nhị phân gốc setuid (ví dụ: /bin/pingcó CAP_NET_RAW, v.v.).

Nguyên vẹn, nhị phân duy nhất tôi còn lại là cái này:

abraxas ~ # find / -xdev -type f -perm -u=s
/usr/lib64/misc/glibc/pt_chown
abraxas ~ # 

Nếu tôi loại bỏ bit setuid hoặc lặp lại hệ thống tập tin gốc của mình nosuid, sshd và GNU Screen ngừng hoạt động, bởi vì chúng gọi grantpt(3)pesudotermutions chính của chúng và glibc dường như thực thi chương trình này để điều khiển và kiểm tra giả hình nô lệ bên dưới /dev/pts/và GNU Screen quan tâm đến chức năng này thất bại

Vấn đề là, trang quản trị đã grantpt(3)tuyên bố rõ ràng rằng trong Linux, với devptshệ thống tập tin được gắn kết, không yêu cầu nhị phân trợ giúp như vậy; hạt nhân sẽ tự động đặt UID & GID của nô lệ thành UID & GID thực của quá trình đã mở /dev/ptmx(bằng cách gọi getpt(3)).

Tôi đã viết một chương trình ví dụ nhỏ để chứng minh điều này:

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
    int master;
    char slave[16];
    struct stat slavestat;
    if ((master = getpt()) < 0) {
        fprintf(stderr, "getpt: %m\n");
        return 1;
    }
    printf("Opened a UNIX98 master terminal, fd = %d\n", master);
    /* I am not going to call grantpt() because I am trying to
     * demonstrate that it is not necessary with devpts mounted,
     * the owners and mode will be set automatically by the kernel.
     */
    if (unlockpt(master) < 0) {
        fprintf(stderr, "unlockpt: %m\n");
        return 2;
    }
    memset(slave, 0, sizeof(slave));
    if (ptsname_r(master, slave, sizeof(slave)) < 0) {
        fprintf(stderr, "ptsname: %m\n");
        return 2;
    }
    printf("Device name of slave pseudoterminal: %s\n", slave);
    if (stat(slave, &slavestat) < 0) {
        fprintf(stderr, "stat: %m\n");
        return 3;
    }
    printf("Information for device %s:\n", slave);
    printf("    Owner UID:  %d\n", slavestat.st_uid);
    printf("    Owner GID:  %d\n", slavestat.st_gid);
    printf("    Octal mode: %04o\n", slavestat.st_mode & 00007777);
    return 0;
}

Quan sát nó trong hành động với bit setuid trên chương trình đã nói ở trên đã bị xóa:

aaron@abraxas ~ $ id
uid=1000(aaron) gid=100(users) groups=100(users)
aaron@abraxas ~ $ ./ptytest 
Opened a UNIX98 master terminal, fd = 3
Device name of slave pseudoterminal: /dev/pts/17
Information for device /dev/pts/17:
    Owner UID:  1000
    Owner GID:  100
    Octal mode: 0620

Tôi chỉ có một vài ý tưởng về cách giải quyết vấn đề này:

1) Thay thế chương trình bằng bộ xương chỉ trả về 0.

2) Patch Grantpt () trong libc của tôi không làm gì cả.

Tôi có thể tự động hóa cả hai thứ này, nhưng có ai có đề xuất cho cái này hơn cái kia không, hoặc đề xuất về cách khác để giải quyết vấn đề này?

Một khi điều này được giải quyết, cuối cùng tôi có thể mount -o remount,nosuid /.


Trong khi chờ phản hồi, tôi đã tiếp cận với cách tiếp cận 1, và sshd và GNU Screen vẫn hoạt động.
Aaron Jones

Chính xác những chương trình thất bại là gì? Có lẽ họ bị hỏng và kiểm tra không phải cho pty(như họ nên) mà cho chương trình?
vonbrand

Bất kỳ chương trình nào không có CAP_CHOWN và CAP_FOWNER, gọi Grantpt () và nhị phân của trình trợ giúp không được bắt đầu với EUID == 0, sẽ có mã trả về khác không cho Grantpt () và các chương trình NÊN hủy bỏ việc tạo PTS khi điều này xảy ra , theo ptmx (4).
Aaron Jones

3
"Giải pháp" đó mang lại cho tôi những ý chí ... trong trường hợp tốt nhất, nó xử lý một lỗi, nó có thể tạo ra một lỗi mới, trong trường hợp xấu nhất nó tạo ra một lỗ hổng bảo mật nghiêm trọng. Xin vui lòng đưa lên với các nhà phát triển glibc.
vonbrand

3
Sau đó báo cáo điều này với người dân glibc.
vonbrand

Câu trả lời:


2

Nếu glibc của bạn là hợp lý hiện tại và devpts được thiết lập chính xác, thì không cần phải gọipt_chown trợ giúp nào cả.

Bạn có thể đang gặp phải một vấn đề đã biết / tiềm năng khi xóa setuid-root khỏi pt_chown.

grantpt()được hỗ trợ devfstừ glibc-2.7 , các thay đổi đã được thực hiện trong glibc-2.11 mặc dù vậy thay vì kiểm tra một cách rõ ràng DEVFS_SUPER_MAGIC, thay vào đó, nó sẽ kiểm tra xem liệu nó có cần thực hiện bất kỳ công việc nào trước khi thử chown()hoặc quay trở lại để gọipt_chown .

Từ glibc-2.17/sysdeps/unix/grantpt.c

  ...
  uid_t uid = __getuid ();
  if (st.st_uid != uid)
    {
       if (__chown (buf, uid, st.st_gid) < 0)
       goto helper;
    }
  ...

Một khổ thơ tương tự được sử dụng để kiểm tra gid và quyền. Điều hấp dẫn là uid, gid và chế độ phải phù hợp với mong đợi (bạn, tty và chính xác là 620; xác nhận với /usr/libexec/pt_chown --help). Nếu không, chown()(sẽ yêu cầu các khả năng CAP_CHOWN, CAP_FOWNER của nhị phân / quy trình gọi) đã được thử và nếu thất bại, trình pt_chowntrợ giúp bên ngoài (phải là setuid-root) đã được thử. Để chopt_chown có thể sử dụng các khả năng, nó (và do đó glibc của bạn) phải được biên dịch với HAVE_LIBCAP. Tuy nhiên , có vẻ như pt_chownlà (như của glibc-2.17 , và như bạn lưu ý mặc dù bạn chưa tuyên bố phiên bản) mã hóa cứng để muốn geteuid()==0 phân biệt các HAVE_LIBCAPmã có liên quan từ glibc-2.17/login/programs/pt_chown.c:

  ...
  if (argc == 1 && euid == 0)
    {
#ifdef HAVE_LIBCAP
  /* Drop privileges.  */
     if (uid != euid)
  ...
#endif
    /* Normal invocation of this program is with no arguments and
       with privileges.  */
    return do_pt_chown ();
  }
...
  /* Check if we are properly installed.  */
  if (euid != 0)
    error (FAIL_EXEC, 0, gettext ("needs to be installed setuid `root'"));

(Mong đợi geteuid()==0 trước khi thử sử dụng các khả năng dường như không thực sự theo tinh thần của các khả năng, tôi sẽ tiến hành đăng nhập một lỗi trên cái này.)

Một cách giải quyết tiềm năng có thể là cung cấp cho CAP_CHOWN, CAP_FOWNER cho các chương trình bị ảnh hưởng, nhưng tôi thực sự không khuyến nghị rằng vì bạn không thể hạn chế điều đó đối với ptys.

Nếu điều đó không giúp bạn giải quyết nó, hãy vá sshdscreenhơi khó chấp nhận hơn so với vá glibc. Vì vấn đề nằm trong glibc, nên cách tiếp cận sạch hơn sẽ là sử dụng chọn lọc DLL để thực hiện một hình nộm grantpt().


"Vì vấn đề nằm trong glibc, nên một cách tiếp cận sạch hơn sẽ là sử dụng có chọn lọc tiêm DLL để thực hiện một cấp giả ()." -- Rực rỡ. Tại sao tôi không nghĩ về điều đó? Cảm ơn. :)
Aaron Jones
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.