GNU/Linux >> Znalost Linux >  >> Linux

Rozdíl mezi linuxovými kapacitami a seccomp

Chci znát přesný rozdíl mezi schopnostmi linuxu a seccomp.

Vysvětlím přesné rozdíly níže, ale zde je obecné vysvětlení:Schopnosti zahrnují různé kontroly ve funkcích jádra dosažitelných systémovými voláními. Pokud se kontrola nezdaří (tj. proces postrádá potřebnou schopnost), syscall obvykle vrátí chybu. Kontrolu lze provést buď přímo na začátku konkrétního systémového volání, nebo hlouběji v jádře v oblastech, které mohou být dosažitelné prostřednictvím více různých systémových volání (jako je zápis do konkrétního privilegovaného souboru).

Seccomp je filtr systémových volání, který se aplikuje na všechna systémová volání před jejich spuštěním. Proces může nastavit filtr, který jim umožní zrušit jejich právo spouštět určitá systémová volání nebo konkrétní argumenty pro určitá systémová volání. Filtr je obvykle ve formě bajtového kódu eBPF, který jádro používá ke kontrole, zda je pro tento proces povoleno systémové volání. Jakmile je filtr použit, nelze jej vůbec uvolnit, pouze zpřísnit (za předpokladu, že systémová volání zodpovědná za načítání zásady seccomp jsou stále povolena).

Všimněte si, že některá systémová volání nemohou být omezena ani seccomp, ani schopnostmi, protože se nejedná o skutečná systémová volání. To je případ volání vDSO, což jsou implementace několika systémových volání v uživatelském prostoru, které nevyžadují striktně jádro. Pokus o zablokování getcpu() nebo gettimeofday() je z tohoto důvodu k ničemu, protože proces bude stejně používat vDSO místo nativního syscall. Naštěstí jsou tato systémová volání (a jejich související virtuální implementace) do značné míry neškodná.

Je to také tím, že schopnosti linuxu používají seccomp interně nebo je to jinak nebo jsou oba úplně odlišné.

Interně jsou implementovány úplně jinak. Jinde jsem napsal další odpověď na současnou implementaci různých technologií sandboxingu a vysvětloval, jak se liší a k čemu jsou.

Schopnosti

Mnoho systémových volání, která provádějí privilegované věci, může zahrnovat interní kontrolu, aby se zajistilo, že volající proces má dostatečné schopnosti. Jádro uchovává seznam schopností, které proces má, a jakmile proces schopnosti zruší, nemůže je získat zpět. Například pokus o zápis do /dev/cpu/*/msr selže, pokud proces volá open() syscall má CAP_SYS_RAWIO . To lze vidět ve zdrojovém kódu jádra odpovědném za úpravu MSR (funkce procesoru na nízké úrovni):

static int msr_open(struct inode *inode, struct file *file)
{
    unsigned int cpu = iminor(file_inode(file));
    struct cpuinfo_x86 *c;

    if (!capable(CAP_SYS_RAWIO))
        return -EPERM;

    if (cpu >= nr_cpu_ids || !cpu_online(cpu))
        return -ENXIO;  /* No such CPU */

    c = &cpu_data(cpu);
    if (!cpu_has(c, X86_FEATURE_MSR))
        return -EIO;    /* MSR not supported */

    return 0;
}

Některá systémová volání se vůbec nespustí pokud není přítomna správná funkce, například vhangup() :

SYSCALL_DEFINE0(vhangup)
{
    if (capable(CAP_SYS_TTY_CONFIG)) {
        tty_vhangup_self();
        return 0;
    }
    return -EPERM;
}

Schopnosti lze považovat za široké třídy privilegovaných funkcí, které lze selektivně odebrat z procesu nebo uživatele. Specifické funkce, které mají kontroly schopností, se liší od verze jádra k verzi jádra a mezi vývojáři jádra často dochází k sporům o to, zda by daná funkce měla vyžadovat schopnosti ke spuštění. Obecně , snížení schopností z procesu zlepšuje zabezpečení snížením počtu privilegovaných akcí, které může provádět. Všimněte si, že některé funkce jsou považovány za root-ekvivalentní , což znamená, že i když zakážete všechny ostatní funkce, mohou být za určitých podmínek použity k opětovnému získání plných oprávnění. Mnoho příkladů uvádí tvůrce grsecurity Brad Spengler. Zřejmým příkladem by bylo CAP_SYS_MODULE což umožňuje načítání libovolných modulů jádra. Jiný by byl CAP_SYS_ADMIN což je univerzální schopnost téměř ekvivalentní rootovi.

Režim 1 seccomp

Existují dva typy seccomp:režim 1 (přísný) a režim 2 (filtr). Režim 1 je extrémně omezující a po aktivaci umožňuje pouze čtyři systémová volání. Tato systémová volání jsou read() , write() , exit() a rt_sigreturn() . Proces je okamžitě odeslán fatální SIGKILL signál z jádra, pokud se někdy pokusí použít systémové volání, které není na seznamu povolených. Tento režim je původním režimem seccomp a nevyžaduje generování a odesílání bajtového kódu eBPF do jádra. Provede se speciální systémové volání, po kterém bude režim 1 aktivní po celou dobu životnosti procesu:seccomp(SECCOMP_SET_MODE_STRICT) nebo prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) . Jakmile je aktivní, nelze jej vypnout.

Následuje příklad programu, který bezpečně spouští bytecode, který vrací 42:

#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>

/* "mov al,42; ret" aka "return 42" */
static const unsigned char code[] = "\xb0\x2a\xc3";

void main(void)
{
    int fd[2], ret;

    /* spawn child process, connected by a pipe */
    pipe(fd);
    if (fork() == 0) {
        close(fd[0]);

        /* enter mode 1 seccomp and execute untrusted bytecode */
        prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
        ret = (*(uint8_t(*)())code)();

        /* send result over pipe, and exit */
        write(fd[1], &ret, sizeof(ret));
        syscall(SYS_exit, 0);
    } else {
        close(fd[1]);

        /* read the result from the pipe, and print it */
        read(fd[0], &ret, sizeof(ret));
        printf("untrusted bytecode returned %d\n", ret);
    }
}

Režim 1 je původní režim a byl přidán za účelem umožnění spouštění nedůvěryhodného bajtkódu pro nezpracované výpočty. Proces zprostředkovatele by rozdělil potomka (a možná by nastavil komunikaci přes roury) a potomek by povolil seccomp, čímž by mu zabránil dělat cokoli, jen číst a zapisovat z deskriptorů souborů, které jsou již otevřené, a ukončovat. Tento podřízený proces by pak mohl bezpečně spustit nedůvěryhodný bajtový kód. Tento režim nepoužívalo mnoho lidí, ale než si Linus mohl stěžovat dostatečně nahlas, aby ho zabil, tým Google Chrome vyjádřil přání použít jej pro svůj prohlížeč. To vyvolalo obnovený zájem o seccomp a zachránilo ho před předčasnou smrtí.

Režim 2 seccomp

Druhý režim, filtr, nazývaný také seccomp-bpf, umožňuje procesu poslat jemnozrnnou politiku filtru do jádra, čímž povolí nebo zakáže celá systémová volání nebo specifické argumenty systémových volání nebo rozsahy argumentů. Zásady také specifikují, co se stane v případě porušení (např. má být proces ukončen nebo má být systémové volání pouze odmítnuto?) a zda má být porušení protokolováno. Protože linuxová systémová volání jsou uchovávána v registrech, a proto mohou být pouze celá čísla, je nemožné filtrovat obsah paměti, na který by mohl odkazovat argument systémového volání. Například, ačkoli můžete zabránit open() před voláním pomocí O_RDWR s možností zápisu nebo O_WRONLY flags, nemůžete přidat na seznam povolených jednotlivou cestu, která je otevřena. Důvodem je to, že pro seccomp není cesta ničím jiným než ukazatelem na paměť obsahující cestu k souborovému systému ukončenou nulou. Neexistuje žádný způsob, jak zaručit, že paměť uchovávající cestu nebyla změněna sourozeneckým vláknem mezi předáním kontroly seccomp a dereferencí ukazatele, kromě toho, že by byla vložena do paměti pouze pro čtení a odepřel k ní přístup systémovým voláním souvisejícím s pamětí. Často je nutné používat LSM jako AppArmor.

Toto je příklad programu, který používá režim 2 seccomp, aby bylo zajištěno, že může vytisknout pouze své aktuální PID. Tento program používá knihovnu libseccomp, která usnadňuje vytváření filtrů seccomp eBPF, i když je také možné to udělat obtížným způsobem bez jakékoli abstraktní knihovny.

#include <seccomp.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

void main(void)
{
    /* initialize the libseccomp context */
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);

    /* allow exiting */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);

    /* allow getting the current pid */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);

    /* allow changing data segment size, as required by glibc */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);

    /* allow writing up to 512 bytes to fd 1 */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 2,
        SCMP_A0(SCMP_CMP_EQ, 1),
        SCMP_A2(SCMP_CMP_LE, 512));

    /* if writing to any other fd, return -EBADF */
    seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EBADF), SCMP_SYS(write), 1,
        SCMP_A0(SCMP_CMP_NE, 1));

    /* load and enforce the filters */
    seccomp_load(ctx);
    seccomp_release(ctx);

    printf("this process is %d\n", getpid());
}

Mode 2 seccomp byl vytvořen, protože režim 1 měl zjevně svá omezení. Ne každou úlohu lze rozdělit do procesu s čistě bajtovým kódem, který by mohl běžet v podřízeném procesu a komunikovat prostřednictvím kanálů nebo sdílené paměti. Tento režim má mnohem více funkcí a jeho funkčnost se stále pomalu rozšiřuje. Má však stále své stinné stránky. Bezpečné používání režimu 2 seccomp vyžaduje hluboké porozumění systémovým voláním (chcete blokovat kill() ze zabíjení jiných procesů? Bohužel můžete zabít procesy pomocí fcntl() také!). Je také křehká, protože změny v základní knihovně libc mohou způsobit poškození. Glibc open() například funkce již vždy nepoužívá systémové volání tohoto jména a místo toho může používat openat() , což porušuje zásady, které zařadily na seznam povolených pouze ty první.


Linux
  1. Rozdíl mezi příkazy „su“ a „su -“ v Linuxu

  2. Rozdíl mezi [[ $a ==Z* ]] a [ $a ==Z* ]?

  3. Linux – rozdíl mezi uživatelským prostorem a prostorem jádra?

  1. Rozdíl mezi absolutní a relativní cestou v Linuxu

  2. linux rozdíl mezi sudo crontab -e a jen crontab -e

  3. Rozdíl mezi příkazy 'ifconfig' a ​​'ip'

  1. Naučte se rozdíl mezi příkazy „su“ a „su -“ v Linuxu

  2. Jaký je rozdíl mezi linuxovým kontejnerem a obrázkem?

  3. Linux – rozdíl mezi restartem, init 6 a vypnutím -r teď?