void priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid) { int pair[2]; /* Create socket pair */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) < 0) { fatal("privsep", "unable to create socket pair for privilege separation"); } priv_unprivileged_fd(pair[0]); priv_privileged_fd(pair[1]); #ifdef ENABLE_PRIVSEP gid_t gidset[1]; int status; /* Spawn off monitor */ if ((monitored = fork()) < 0) fatal("privsep", "unable to fork monitor"); switch (monitored) { case 0: /* We are in the children, drop privileges */ if (RUNNING_ON_VALGRIND) log_warnx("privsep", "running on valgrind, keep privileges"); else { priv_setup_chroot(chrootdir); if (chroot(chrootdir) == -1) fatal("privsep", "unable to chroot"); if (chdir("/") != 0) fatal("privsep", "unable to chdir"); gidset[0] = gid; #ifdef HAVE_SETRESGID if (setresgid(gid, gid, gid) == -1) fatal("privsep", "setresgid() failed"); #else if (setregid(gid, gid) == -1) fatal("privsep", "setregid() failed"); #endif if (setgroups(1, gidset) == -1) fatal("privsep", "setgroups() failed"); #ifdef HAVE_SETRESUID if (setresuid(uid, uid, uid) == -1) fatal("privsep", "setresuid() failed"); #else if (setreuid(uid, uid) == -1) fatal("privsep", "setreuid() failed"); #endif } close(pair[1]); priv_ping(); break; default: /* We are in the monitor */ if (ctl != -1) close(ctl); close(pair[0]); if (atexit(priv_exit) != 0) fatal("privsep", "unable to set exit function"); /* Install signal handlers */ const struct sigaction pass_to_child = { .sa_handler = sig_pass_to_chld, .sa_flags = SA_RESTART }; sigaction(SIGALRM, &pass_to_child, NULL); sigaction(SIGTERM, &pass_to_child, NULL); sigaction(SIGHUP, &pass_to_child, NULL); sigaction(SIGINT, &pass_to_child, NULL); sigaction(SIGQUIT, &pass_to_child, NULL); const struct sigaction child = { .sa_handler = sig_chld, .sa_flags = SA_RESTART }; sigaction(SIGCHLD, &child, NULL); if (waitpid(monitored, &status, WNOHANG) != 0) /* Child is already dead */ _exit(1); priv_loop(pair[1], 0); exit(0); } #else log_warnx("priv", "no privilege separation available"); priv_ping(); #endif }
/* Initialization */ void priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid) { int pair[2]; gid_t gidset[1]; int status; /* Create socket pair */ if (socketpair(AF_LOCAL, SOCK_DGRAM, PF_UNSPEC, pair) < 0) fatal("privsep", "unable to create socket pair for privilege separation"); /* Spawn off monitor */ if ((monitored = fork()) < 0) fatal("privsep", "unable to fork monitor"); switch (monitored) { case 0: /* We are in the children, drop privileges */ if (RUNNING_ON_VALGRIND) log_warnx("privsep", "running on valgrind, keep privileges"); else { struct stat schroot; if (stat(chrootdir, &schroot) == -1) { if (errno != ENOENT) fatal("privsep", "chroot directory does not exist"); if (mkdir(chrootdir, 0755) == -1) fatal("privsep", "unable to create chroot directory"); log_info("privsep", "created chroot directory %s", chrootdir); } if (chroot(chrootdir) == -1) fatal("privsep", "unable to chroot"); if (chdir("/") != 0) fatal("privsep", "unable to chdir"); gidset[0] = gid; #ifdef HAVE_SETRESGID if (setresgid(gid, gid, gid) == -1) fatal("privsep", "setresgid() failed"); #else if (setregid(gid, gid) == -1) fatal("privsep", "setregid() failed"); #endif if (setgroups(1, gidset) == -1) fatal("privsep", "setgroups() failed"); #ifdef HAVE_SETRESUID if (setresuid(uid, uid, uid) == -1) fatal("privsep", "setresuid() failed"); #else if (setreuid(uid, uid) == -1) fatal("privsep", "setreuid() failed"); #endif } priv_remote(pair[0]); close(pair[1]); priv_ping(); break; default: /* We are in the monitor */ if (ctl != -1) close(ctl); priv_remote(pair[1]); close(pair[0]); if (atexit(priv_exit) != 0) fatal("privsep", "unable to set exit function"); /* Install signal handlers */ const struct sigaction pass_to_child = { .sa_handler = sig_pass_to_chld, .sa_flags = SA_RESTART }; sigaction(SIGALRM, &pass_to_child, NULL); sigaction(SIGTERM, &pass_to_child, NULL); sigaction(SIGHUP, &pass_to_child, NULL); sigaction(SIGINT, &pass_to_child, NULL); sigaction(SIGQUIT, &pass_to_child, NULL); const struct sigaction child = { .sa_handler = sig_chld, .sa_flags = SA_RESTART }; sigaction(SIGCHLD, &child, NULL); if (waitpid(monitored, &status, WNOHANG) != 0) /* Child is already dead */ _exit(1); priv_loop(); exit(0); } }