возможность из другого пространства имен пользователя - PullRequest
0 голосов
/ 04 декабря 2018

Я изучаю возможности posix и пространство имен в linux и написал несколько строк кода, вдохновленных этими впечатляющими статьями , чтобы лучше понять, как эти возможности видятся в разных пространствах имен.Некоторая часть кода взята из примеров статьи, а не из моей игры ...

#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sched.h>
#include <sys/capability.h>
#include "caputilities.h"


#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
#define MAXLEN 255

/* Replace commas in mapping string arguments with newlines */
static void get_mapstr(char *map){
    if (map==NULL) return;
    size_t map_len = strlen(map);
    for (int j = 0; j < map_len; j++)
        if (map[j] == ',') map[j] = '\n';
}

static void save_map(char *map, char *map_file){
    int fd;
    fd = open(map_file, O_RDWR);
    if (fd == -1) {
        fprintf(stderr, "open %s: %s\n", map_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t map_len = strlen(map);
    if (write(fd, map, map_len) != map_len) {
        fprintf(stderr, "write %s: %s\n", map_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    close(fd);
}

/* Start function for cloned child */
static int childFunc(void *arg){
    pid_t pid = getpid();
    fprintf(stderr, "cloned child pid %ld\n", (long)pid);
    fprintf(stderr, "child process capabilities %s\n", cap_to_text(cap_get_proc(), NULL));
    fprintf(stderr, "euid %ld, egid %ld\n", (long)geteuid(), (long)getegid());
    if (arg!=NULL){ //user ns enabled 
        char *uidmap = ((char **)arg)[0];
        char *gidmap = ((char **)arg)[1];
        if (uidmap!=NULL) fprintf(stderr, "setting uid map %s\n", uidmap);
        if (gidmap!=NULL) fprintf(stderr, "setting gid map %s\n", gidmap);
        char map_path[MAXLEN + 1];
        if (uidmap != NULL){
            snprintf(map_path, MAXLEN, "/proc/%ld/uid_map", (long)pid);
            save_map(uidmap, map_path);
        }
        if (gidmap != NULL){
            snprintf(map_path, MAXLEN, "/proc/%ld/gid_map", (long)pid);
            save_map(gidmap, map_path);
        }
        fprintf(stderr, "child process capabilities %s\n", cap_to_text(cap_get_proc(), NULL));
        fprintf(stderr, "euid %ld, egid %ld\n", (long)geteuid(), (long)getegid());
    }
    sleep(200);
    exit(0);
}

static void usage(char *pname){
    fprintf(stderr, "Usage: %s -U -M mapstring -G mapstring\n", pname);
    fprintf(stderr, "       -U use user namespace\n");
    fprintf(stderr, "       -M uid mapping\n");
    fprintf(stderr, "       -G gid mapping\n");
    fprintf(stderr, "       mapstring is a comma separated list of mapping of the form:\n");
    fprintf(stderr, "       ID_inside-ns    ID-outside-ns   length [,ID_inside-ns    ID-outside-ns   length, ...]\n");
    exit(EXIT_FAILURE);
}

#define STACK_SIZE (1024 * 1024)

static char child_stack[STACK_SIZE];    /* Space for child's stack */

/* Receive a UID and/or GID mapping as arguments
   Every mapping consists of a list of tuple (separated by new line) of the form:
       ID_inside-ns    ID-outside-ns   length
   Requiring the user to supply a string that contains newlines is
   of course inconvenient for command-line use. Thus, we permit the
   use of commas to delimit records in this string, and replace them
   with newlines before writing the string to the file. */
int main(int argc, char *argv[]){
    int flags = 0;
    char *gid_map = NULL, *uid_map = NULL;
    int opt;
    while ((opt = getopt(argc, argv, "UM:G:")) != -1) {
        switch (opt){
            case 'U': flags |= CLONE_NEWUSER;
            case 'M': uid_map = optarg; break;
            case 'G': gid_map = optarg; break;
            default: usage(argv[0]);
        }
    }
    if ((uid_map != NULL || gid_map != NULL) && !(flags & CLONE_NEWUSER)){
        fprintf(stderr,"what about give me the user namespace option? what's in your mind today?\n");
        usage(argv[0]);
    } 
    char* args[2];
    get_mapstr(uid_map); args[0] = uid_map;
    get_mapstr(gid_map); args[1] = gid_map; 
    pid_t child_pid = clone(childFunc, child_stack + STACK_SIZE, flags | SIGCHLD, (flags & CLONE_NEWUSER) ? &args : NULL);
    if (child_pid == -1) errExit("clone");
    sleep(1);
    fprintf(stderr, "child process pid capabilities from parent: %s\n", cap_to_text(cap_get_pid(child_pid), NULL));
    fprintf(stderr, "euid %ld, egid %ld\n", (long)geteuid(), (long)getegid());
    exit(0);
}

Я доказал, что из дочернего элемента в новом пространстве имен можно сопоставить эффективный идентификатор пользователя только с внешнимПространство имен родительского процесса с любым идентификатором пользователя в новом пространстве имен, включая root, но если вы попытаетесь сопоставить других внешних пользователей с дочерним, вы получите ошибку.Это нормально.

$ ./testcap3 -U -M"1000 39 1"
cloned child pid 7659
child process capabilities = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
euid 65534, egid 65534
setting uid map 1000 39 1
write /proc/7659/uid_map: Operation not permitted
child process pid capabilities from parent: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
euid 1000, egid 1000
$ ./testcap3 -U -M"0 1000 1"
cloned child pid 7665
child process capabilities = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
euid 65534, egid 65534
setting uid map 0 1000 1
child process capabilities = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
euid 0, egid 65534
child process pid capabilities from parent: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
euid 1000, egid 1000

Я не понимаю, почему возможности дочернего процесса отображаются как все включенные при печати из родительского процесса.Я бы не ожидал увидеть привилегий во внешнем пространстве имен, я не прав?Ясно, что двоичный testcap3 не является привилегированным (ни бит setuid / setgid, ни возможности не установлены в файле, и эффективный пользователь не является администратором) Как хранятся возможности?Как структуры данных связаны с пространством имен?

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Я немного изменил тестовый код, чтобы попытаться уничтожить клонированного потомка в новом пространстве имен, обнаружив ошибку разрешения, как и ожидалось.
Поэтому у меня была возможность покопаться в коде ядра, чтобы проанализировать, как убить авторизациюпредоставлены / запрещены.
Ядро сравнивает пространство имен процесса, подлежащего уничтожению, с пространством имен текущего потока, если они совпадают с ним, проверяет, имеет ли текущий поток активный флаг для уничтожения.
В противном случае (не совпадает пространство имен)он проверяет, является ли текущий поток предком процесса, который создал пространство имен процесса для уничтожения, если это так, он позволяет продолжить оценку других модулей безопасности Linux, если таковые имеются.
Наоборот, если поток-убийца является потомкомцелевой процесс и не находится в том же пространстве имен процесса, в лицензии на уничтожение которого отказано.

glibc определяет слабый символ для вызова kill userspace, определенного в singnal.h, поэтому я предполагаю, что вызываемый кодопределены на уровне ядра, этоСистемные вызовы:

системный вызов для уничтожения

group_send_sig_info

check_kill_permission

kill_ok_by_cred

подключить к способному для lsm модуль возможностей

0 голосов
/ 04 декабря 2018

Я просмотрел код возможности, чтобы узнать структуры.

библиотеки уровня пользователя используют вызовы, определенные в <sys/capability.h>, для извлечения набора возможностей, в частности, все функции libcap, определенные в cappro.c использовать функцию capget для извлечения структур данных cap_user_header_t и cap_user_data_t, определенных в <sys/capability.h> Capget системного вызова определен в abilities.c , целью является обновление данныхструктура, указанная dataptr (второй аргумент системного вызова) с набором возможностей процесса &header->pid (переданный первым параметром), есть некоторый шаблонный код для копирования переменных из пространства ядра в пространство пользователя и наоборот ..
Ключ вызова cap_get_target_pid передает по адресу эффективные, разрешенные, наследуемые наборы возможностей.Функция cap_get_target_pid загружает структуру задачи для пространства имен pid pid, полученного аргументом, благодаря функциям task_pid_vnr и find_task_by_vpid .В начальной проверке используется переменная current , которая определяет текущую задачу в процессе выполнения.Функция security_capget использует платформу LSM, которая вызывает ловушку capget cap_capget , которая показывает, где набор извлекается .. они сохраняются в поле учетных данных структуры задачи (должна быть другая структура задачи длякаждое пространство имен pid).Хуки для шапки модуля определены в конце файла commoncap.c В любом случае, опять же я не понимаю, почему он не может писать в файл сопоставления разных пользователей, если для него установлены все возможностив пространстве имен родительского pid.Все еще озадачен.

...