Подтвердить что ты не робот

Удаление привилегий root

У меня есть демон, который запускается как root (поэтому он может связываться с низкими портами). После инициализации я бы очень хотел, чтобы он сбросил права root по соображениям безопасности.

Может ли кто-нибудь указать мне на известную правильную часть кода в C, которая сделает это?

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

Для справки: я начинаю с root; Мне нужно изменить, чтобы работать под другим uid и gid; Я должен правильно настроить дополнительные группы; После этого мне не нужно менять права root.

4b9b3361

Ответ 2

Чтобы отказаться от всех привилегий (пользователя и группы), вам необходимо отбросить группу перед пользователем. Учитывая, что userid и groupid содержат идентификаторы пользователя и группы, к которой вы хотите перейти, и считая, что эффективные идентификаторы также являются корневыми, это достигается путем вызова setuid() и 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));
}

Если вы параноик, вы можете попытаться вернуть свои привилегии root, что должно завершиться неудачей. Если это не сработает, вы получите помощь:

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

Кроме того, если вы все еще параноик, вы можете seteuid() и setegid(), но это не обязательно, поскольку setuid() и setgid() уже установили все идентификаторы, если процесс принадлежит root.

Список дополнительных групп - проблема, потому что нет функции POSIX для установки дополнительных групп (есть getgroups(), но нет групп групп()). Существует расширение BSD и Linux setgroups(), которое вы можете использовать, это вас касается.

Вы также должны chdir("/") или в любой другой каталог, чтобы процесс не оставался в корневом каталоге.

Поскольку ваш вопрос касается Unix вообще, это очень общий подход. Обратите внимание: в Linux это уже не предпочтительный подход. В текущих версиях Linux вы должны установить CAP_NET_BIND_SERVICE возможность в исполняемом файле и запустить его как обычного пользователя. Нет необходимости в корневом доступе.

Ответ 3

Вот что я мог сделать лучше всего:

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