2010-07-28 18 views
45

Mam demona, który uruchamia się jako root (więc może wiązać się z małymi portami). Po inicjalizacji bardzo chciałbym, aby z powodu bezpieczeństwa usunęło przywileje roota.Upuszczanie uprawnień roota

Czy ktoś może wskazać mi na znany poprawny kod w C, który to zrobi?

Przeczytałem strony podręcznika użytkownika, zapoznałem się z różnymi wersjami tego w różnych aplikacjach i wszystkie są różne, a niektóre z nich są naprawdę skomplikowane. To jest kod związany z bezpieczeństwem i naprawdę nie chcę wymyślać tych samych błędów, które popełniają inni. To, czego szukam, to najlepsza praktyka, znana dobra, przenośna funkcja biblioteczna, z której mogę korzystać, wiedząc, że to się uda. Czy coś takiego istnieje?

Dla porównania: Zaczynam jako root; Muszę się zmienić, aby działać pod innym uid i gid; Potrzebuję prawidłowo utworzonych grup uzupełniających; Nie muszę później zmieniać przywilejów roota.

+5

To bardzo różni się między Unixami - czy są jakieś szczególne? Jeśli potrzebujesz "przenośnego" rozwiązania, będzie to nieprzyjemne, a Ty najlepiej sobie z tym poradzisz, np. funkcja permanently_set_uid() z OpenSSH - w uidswap.c plik – nos

Odpowiedz

43

Aby zrzucić wszystkie uprawnienia (użytkownika i grupy), należy usunąć grupę przed użytkownikiem. Biorąc pod uwagę, że userid i groupid zawiera identyfikatory użytkownika i grupy, które chcesz spaść, i przy założeniu, że skuteczne identyfikatory są również korzeń, jest to realizowane poprzez wywołanie setuid() i setgid():

if (getuid() == 0) { 
    /* process is running as root, drop privileges */ 
    if (setgid(groupid) != 0) 
     fatal("setgid: Unable to drop group privileges: %s", strerror(errno)); 
    if (setuid(userid) != 0) 
     fatal("setuid: Unable to drop user privileges: %S", strerror(errno)); 
} 

Jeśli jesteś paranoikiem , możesz spróbować odzyskać przywileje roota, co powinno zakończyć się niepowodzeniem. Jeśli się nie powiedzie, ty ratunkowy:

if (setuid(0) != -1) 
    fatal("ERROR: Managed to regain root privileges?"); 

Ponadto, jeśli nadal jesteś paranoikiem, możesz chcieć seteuid() i setegid() też, ale to nie powinno być konieczne, ponieważ setuid() i setgid() już ustawić wszystkie identyfikatory, jeśli proces jest własnością root.

Dodatkowa lista grup jest problemem, ponieważ nie ma funkcji POSIX do ustawienia grup uzupełniających (istnieje getgroups(), ale nie ma setgroups()). Istnieje rozszerzenie BSD i Linux setgroups(), którego możesz użyć, to dotyczy ciebie.

Powinieneś również chdir("/") lub do dowolnego innego katalogu, aby proces nie pozostał w katalogu należącym do użytkownika root.

Ponieważ twoje pytanie dotyczy w ogóle Unixa, jest to podejście bardzo ogólne. Zauważ, że w Linuksie nie jest to już preferowane podejście. W obecnych wersjach systemu Linux, należy ustawić CAP_NET_BIND_SERVICE capability na pliku wykonywalnym i uruchomić go jako zwykłego użytkownika. Brak dostępu do roota.

+1

Ty też chciałbyś ustawić gid - i to może się nieznacznie różnić w uniksach, jeśli chodzi o konfigurację setuid/setgid w rzeczywistości z rzeczywistymi i zapisanymi identyfikatorami. – nos

+1

To naprawdę musi być przenośne rozwiązanie, więc nie ma Linuxa Możliwości fu dozwolone, obawiam się. I rzeczywiście spróbowałem prostego podejścia za pomocą setuid() i setgid(); nie ustawia poprawnie grup (i jeśli nie wywołasz setgroups(), najwyraźniej możesz nadal być członkiem niektórych grup root'a!). –

+0

@nos Dzięki. Rozszerzony, aby objąć grupy. Jeśli proces jest własnością root (jak wspomniano OP) lub jeśli jest to root-setuid, to setuid() i setgid() już ustawiają wszystkie identyfikatory (rzeczywiste, efektywne i zapisane). To jest w specyfikacji. W przeciwnym razie implementacja nie byłaby zgodna z POSIX. – Juliano

1

To co mogę zrobić najlepiej:

#define _GNU_SOURCE // for secure_getenv() 


int drop_root_privileges(void) { // returns 0 on success and -1 on failure 
    gid_t gid; 
    uid_t uid; 

    // no need to "drop" the privileges that you don't have in the first place! 
    if (getuid() != 0) { 
     return 0; 
    } 

    // when your program is invoked with sudo, getuid() will return 0 and you 
    // won't be able to drop your privileges 
    if ((uid = getuid()) == 0) { 
     const char *sudo_uid = secure_getenv("SUDO_UID"); 
     if (sudo_uid == NULL) { 
      printf("environment variable `SUDO_UID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     uid = (uid_t) strtoll(sudo_uid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_UID` to integer"); 
      return -1; 
     } 
    } 

    // again, in case your program is invoked using sudo 
    if ((gid = getgid()) == 0) { 
     const char *sudo_gid = secure_getenv("SUDO_GID"); 
     if (sudo_gid == NULL) { 
      printf("environment variable `SUDO_GID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     gid = (gid_t) strtoll(sudo_gid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_GID` to integer"); 
      return -1; 
     } 
    } 

    if (setgid(gid) != 0) { 
     perror("setgid"); 
     return -1; 
    } 
    if (setuid(uid) != 0) { 
     perror("setgid"); 
     return -1;  
    } 

    // change your directory to somewhere else, just in case if you are in a 
    // root-owned one (e.g. /root) 
    if (chdir("/") != 0) { 
     perror("chdir"); 
     return -1; 
    } 

    // check if we successfully dropped the root privileges 
    if (setuid(0) == 0 || seteuid(0) == 0) { 
     printf("could not drop root privileges!\n"); 
     return -1; 
    } 

    return 0; 
} 
Powiązane problemy