Процесс возврата прав root


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

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

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

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

3 51

3 ответа:

вы ищете эту статью:

POS36-C. соблюдайте правильный порядок отзыва при отказе от привилегий

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

чтобы удалить все привилегии (пользователь и группа), вам нужно удалить группу перед пользователем. Учитывая, что 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 (), но не setgroups()). Существует расширение BSD и Linux setgroups() что вы можете использовать, это касается вас.

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

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

Это было то, что я мог бы сделать лучше:

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