static void privupdate_self(void) { int set; if (mac_aware) { if (setpflags(NET_MAC_AWARE, 1) != 0) fatal("setpflags(NET_MAC_AWARE)"); if (setpflags(NET_MAC_AWARE_INHERIT, 1) != 0) fatal("setpflags(NET_MAC_AWARE_INHERIT)"); } if (pfexec) { if (setpflags(PRIV_PFEXEC, 1) != 0) fatal("setpflags(PRIV_PFEXEC)"); } if (sets != NULL) { priv_set_t *target = priv_allocset(); if (target == NULL) fatal("priv_allocet"); set = priv_getsetbyname(PRIV_INHERITABLE); if (rem[set] != NULL || add[set] != NULL || assign[set] != NULL) { (void) getppriv(PRIV_INHERITABLE, target); if (rem[set] != NULL) priv_intersect(rem[set], target); if (add[set] != NULL) priv_union(add[set], target); if (assign[set] != NULL) priv_copyset(assign[set], target); if (setppriv(PRIV_SET, PRIV_INHERITABLE, target) != 0) fatal("setppriv(Inheritable)"); } set = priv_getsetbyname(PRIV_LIMIT); if (rem[set] != NULL || add[set] != NULL || assign[set] != NULL) { (void) getppriv(PRIV_LIMIT, target); if (rem[set] != NULL) priv_intersect(rem[set], target); if (add[set] != NULL) priv_union(add[set], target); if (assign[set] != NULL) priv_copyset(assign[set], target); if (setppriv(PRIV_SET, PRIV_LIMIT, target) != 0) fatal("setppriv(Limit)"); } priv_freeset(target); } if (Doff || Don) (void) setpflags(PRIV_DEBUG, Don ? 1 : 0); if (xpol) (void) setpflags(PRIV_XPOLICY, 1); if (pfexec) (void) setpflags(PRIV_PFEXEC, 1); }
static int priv_set_initialize (void) { if (! initialized) { eff_set = priv_allocset (); if (!eff_set) { return -1; } rem_set = priv_allocset (); if (!rem_set) { priv_freeset (eff_set); return -1; } if (getppriv (PRIV_EFFECTIVE, eff_set) != 0) { priv_freeset (eff_set); priv_freeset (rem_set); return -1; } priv_emptyset (rem_set); initialized = true; } return 0; }
int main (void) { #if HAVE_GETPPRIV && HAVE_PRIV_H priv_set_t *set; ASSERT (set = priv_allocset ()); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); /* Do a series of removes and restores making sure that the results are consistent with our ismember function and solaris' priv_ismember. */ ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); ASSERT (priv_set_restore (PRIV_PROC_EXEC) == -1); ASSERT (errno == EINVAL); ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); ASSERT (priv_set_remove (PRIV_PROC_EXEC) == 0); ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 0); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 0); ASSERT (priv_set_remove (PRIV_PROC_EXEC) == -1); ASSERT (errno == EINVAL); ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 0); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 0); ASSERT (priv_set_restore (PRIV_PROC_EXEC) == 0); ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); ASSERT (priv_set_restore (PRIV_PROC_EXEC) == -1); ASSERT (errno == EINVAL); ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); /* Test the priv_set_linkdir wrappers. */ ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); if (priv_ismember (set, PRIV_SYS_LINKDIR)) { ASSERT (priv_set_restore_linkdir () == -1); ASSERT (errno == EINVAL); ASSERT (priv_set_remove_linkdir () == 0); ASSERT (priv_set_remove_linkdir () == -1); ASSERT (errno == EINVAL); ASSERT (priv_set_restore_linkdir () == 0); } #else ASSERT (priv_set_restore_linkdir () == -1); ASSERT (priv_set_remove_linkdir () == -1); #endif return 0; }
static void change_user_set_privs(void) { priv_set_t *priv_set; priv_set = priv_allocset(); if (getppriv(PRIV_PERMITTED, priv_set) == -1) { dprintf("getppriv %s", strerror(errno)); } else { char *p; p = priv_set_to_str(priv_set, ',', 0); dprintf("started with privs %s", p != NULL ? p : "Unknown"); free(p); } priv_freeset(priv_set); /* always start with the basic set */ priv_set = priv_str_to_set("basic", ",", NULL); if (priv_set == NULL) { syslog(LOG_ERR, "converting basic privilege set: %m"); exit(EXIT_FAILURE); } (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); (void) priv_addset(priv_set, PRIV_PROC_AUDIT); (void) priv_addset(priv_set, PRIV_PROC_OWNER); (void) priv_addset(priv_set, PRIV_PROC_SETID); (void) priv_addset(priv_set, PRIV_SYS_CONFIG); (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { syslog(LOG_ERR, "setppriv inheritable: %m"); priv_freeset(priv_set); exit(EXIT_FAILURE); } if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { syslog(LOG_ERR, "setppriv permitted: %m"); priv_freeset(priv_set); exit(EXIT_FAILURE); } if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { syslog(LOG_ERR, "setppriv effective: %m"); priv_freeset(priv_set); exit(EXIT_FAILURE); } priv_freeset(priv_set); }
void solaris_drop_privs_pinfo_net_fork_exec(void) { priv_set_t *pset = NULL, *npset = NULL; /* * Note: this variant avoids dropping DAC filesystem rights, in case * the process calling it is running as root and should have the * ability to read/write/chown any file on the system. * * We start with the basic set, then *add* the DAC rights to it while * taking away other parts of BASIC we don't need. Then we intersect * this with our existing PERMITTED set. In this way we keep any * DAC rights we had before, while otherwise reducing ourselves to * the minimum set of privileges we need to proceed. * * This also means we drop any other parts of "root" that we don't * need (e.g. the ability to kill any process, create new device nodes * etc etc). */ if ((pset = priv_allocset()) == NULL || (npset = priv_allocset()) == NULL) fatal("priv_allocset: %s", strerror(errno)); priv_basicset(npset); if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 || priv_addset(npset, PRIV_FILE_DAC_READ) != 0 || priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 || priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 || priv_addset(npset, PRIV_FILE_OWNER) != 0) fatal("priv_addset: %s", strerror(errno)); if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 || priv_delset(npset, PRIV_NET_ACCESS) != 0 || priv_delset(npset, PRIV_PROC_EXEC) != 0 || priv_delset(npset, PRIV_PROC_FORK) != 0 || priv_delset(npset, PRIV_PROC_INFO) != 0 || priv_delset(npset, PRIV_PROC_SESSION) != 0) fatal("priv_delset: %s", strerror(errno)); if (getppriv(PRIV_PERMITTED, pset) != 0) fatal("getppriv: %s", strerror(errno)); priv_intersect(pset, npset); if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 || setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 || setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0) fatal("setppriv: %s", strerror(errno)); priv_freeset(pset); priv_freeset(npset); }
static int smb_init_daemon_priv(int flags, uid_t uid, gid_t gid) { priv_set_t *perm = NULL; int ret = -1; char buf[1024]; /* * This is not a significant failure: it allows us to start programs * with sufficient privileges and with the proper uid. We don't * care enough about the extra groups in that case. */ if (flags & PU_RESETGROUPS) (void) setgroups(0, NULL); if (gid != (gid_t)-1 && setgid(gid) != 0) goto end; perm = priv_allocset(); if (perm == NULL) goto end; /* E = P */ (void) getppriv(PRIV_PERMITTED, perm); (void) setppriv(PRIV_SET, PRIV_EFFECTIVE, perm); /* Now reset suid and euid */ if (uid != (uid_t)-1 && setreuid(uid, uid) != 0) goto end; /* I = 0 */ priv_emptyset(perm); ret = setppriv(PRIV_SET, PRIV_INHERITABLE, perm); end: priv_freeset(perm); if (core_get_process_path(buf, sizeof (buf), getpid()) == 0 && strcmp(buf, "core") == 0) { if ((uid == (uid_t)-1 ? geteuid() : uid) == 0) { (void) core_set_process_path(root_cp, sizeof (root_cp), getpid()); } else { (void) core_set_process_path(daemon_cp, sizeof (daemon_cp), getpid()); } } (void) setpflags(__PROC_PROTECT, 0); return (ret); }
int mtev_security_setcaps(mtev_security_captype_t type, const char *capstring) { #ifndef CAP_SUPPORTED mtevL(mtev_error, "Capabilities not supported on this platform.\n"); return -1; #endif #ifdef HAVE_SETPPRIV int rv; const char *endptr; char *str; priv_set_t *set, *old; priv_ptype_t ptype = "Permitted"; set = priv_str_to_set(capstring, ",", &endptr); if(!set) { mtevL(mtev_error, "Cannot translate '%s' to privilege set.\n", capstring); return -1; } switch(type) { case MTEV_SECURITY_CAP_PERMITTED: ptype = "Permitted"; break; case MTEV_SECURITY_CAP_EFFECTIVE: ptype = "Effective"; break; case MTEV_SECURITY_CAP_INHERITABLE: ptype = "Inheritable"; break; } old = priv_allocset(); getppriv(ptype, old); str = priv_set_to_str(old, ',', PRIV_STR_PORT); mtevL(mtev_debug, "Old privs(%s) -> %s\n", ptype, str); priv_freeset(old); free(str); rv = setppriv(PRIV_SET, ptype, set); str = priv_set_to_str(set, ',', PRIV_STR_PORT); mtevL(mtev_debug, "%s privs(%s) -> %s\n", (rv == 0) ? "Changed to" : "Failed to change to", ptype, str); free(str); priv_freeset(set); return rv; #else return -1; #endif }
/* * Privilege system call entry point */ int privsys(int code, priv_op_t op, priv_ptype_t type, void *buf, size_t bufsize, int itype) { int retv; extern int issetugid(void); switch (code) { case PRIVSYS_SETPPRIV: if (bufsize < sizeof (priv_set_t)) return (set_errno(ENOMEM)); return (setppriv(op, type, buf)); case PRIVSYS_GETPPRIV: if (bufsize < sizeof (priv_set_t)) return (set_errno(ENOMEM)); return (getppriv(type, buf)); case PRIVSYS_GETIMPLINFO: return (getprivimplinfo(buf, bufsize)); case PRIVSYS_SETPFLAGS: retv = setpflags((uint_t)op, (uint_t)type, NULL); return (retv != 0 ? set_errno(retv) : 0); case PRIVSYS_GETPFLAGS: retv = (int)getpflags((uint_t)op, CRED()); return (retv == -1 ? set_errno(EINVAL) : retv); case PRIVSYS_ISSETUGID: return (issetugid()); case PRIVSYS_KLPD_REG: if (bufsize < sizeof (priv_set_t)) return (set_errno(ENOMEM)); return ((int)klpd_reg((int)op, (idtype_t)itype, (id_t)type, buf)); case PRIVSYS_KLPD_UNREG: return ((int)klpd_unreg((int)op, (idtype_t)itype, (id_t)type)); case PRIVSYS_PFEXEC_REG: return ((int)pfexec_reg((int)op)); case PRIVSYS_PFEXEC_UNREG: return ((int)pfexec_unreg((int)op)); } return (set_errno(EINVAL)); }
int ntpdmain( int argc, char *argv[] ) { l_fp now; struct recvbuf *rbuf; const char * logfilename; # ifdef HAVE_UMASK mode_t uv; # endif # if defined(HAVE_GETUID) && !defined(MPE) /* MPE lacks the concept of root */ uid_t uid; # endif # if defined(HAVE_WORKING_FORK) long wait_sync = 0; int pipe_fds[2]; int rc; int exit_code; # ifdef _AIX struct sigaction sa; # endif # if !defined(HAVE_SETSID) && !defined (HAVE_SETPGID) && defined(TIOCNOTTY) int fid; # endif # endif /* HAVE_WORKING_FORK*/ # ifdef SCO5_CLOCK int fd; int zero; # endif # ifdef NEED_PTHREAD_WARMUP my_pthread_warmup(); # endif # ifdef HAVE_UMASK uv = umask(0); if (uv) umask(uv); else umask(022); # endif saved_argc = argc; saved_argv = argv; progname = argv[0]; initializing = TRUE; /* mark that we are initializing */ parse_cmdline_opts(&argc, &argv); # ifdef DEBUG debug = OPT_VALUE_SET_DEBUG_LEVEL; # ifdef HAVE_SETLINEBUF setlinebuf(stdout); # endif # endif if (HAVE_OPT(NOFORK) || HAVE_OPT(QUIT) # ifdef DEBUG || debug # endif || HAVE_OPT(SAVECONFIGQUIT)) nofork = TRUE; init_logging(progname, NLOG_SYNCMASK, TRUE); /* honor -l/--logfile option to log to a file */ if (HAVE_OPT(LOGFILE)) { logfilename = OPT_ARG(LOGFILE); syslogit = FALSE; change_logfile(logfilename, FALSE); } else { logfilename = NULL; if (nofork) msyslog_term = TRUE; if (HAVE_OPT(SAVECONFIGQUIT)) syslogit = FALSE; } msyslog(LOG_NOTICE, "%s: Starting", Version); { int i; char buf[1024]; /* Secret knowledge of msyslog buf length */ char *cp = buf; /* Note that every arg has an initial space character */ snprintf(cp, sizeof(buf), "Command line:"); cp += strlen(cp); for (i = 0; i < saved_argc ; ++i) { snprintf(cp, sizeof(buf) - (cp - buf), " %s", saved_argv[i]); cp += strlen(cp); } msyslog(LOG_INFO, "%s", buf); } /* * Install trap handlers to log errors and assertion failures. * Default handlers print to stderr which doesn't work if detached. */ isc_assertion_setcallback(assertion_failed); isc_error_setfatal(library_fatal_error); isc_error_setunexpected(library_unexpected_error); /* MPE lacks the concept of root */ # if defined(HAVE_GETUID) && !defined(MPE) uid = getuid(); if (uid && !HAVE_OPT( SAVECONFIGQUIT )) { msyslog_term = TRUE; msyslog(LOG_ERR, "must be run as root, not uid %ld", (long)uid); exit(1); } # endif /* * Enable the Multi-Media Timer for Windows? */ # ifdef SYS_WINNT if (HAVE_OPT( MODIFYMMTIMER )) set_mm_timer(MM_TIMER_HIRES); # endif #ifdef HAVE_DNSREGISTRATION /* * Enable mDNS registrations? */ if (HAVE_OPT( MDNS )) { mdnsreg = TRUE; } #endif /* HAVE_DNSREGISTRATION */ if (HAVE_OPT( NOVIRTUALIPS )) listen_to_virtual_ips = 0; /* * --interface, listen on specified interfaces */ if (HAVE_OPT( INTERFACE )) { int ifacect = STACKCT_OPT( INTERFACE ); const char** ifaces = STACKLST_OPT( INTERFACE ); sockaddr_u addr; while (ifacect-- > 0) { add_nic_rule( is_ip_address(*ifaces, AF_UNSPEC, &addr) ? MATCH_IFADDR : MATCH_IFNAME, *ifaces, -1, ACTION_LISTEN); ifaces++; } } if (HAVE_OPT( NICE )) priority_done = 0; # ifdef HAVE_SCHED_SETSCHEDULER if (HAVE_OPT( PRIORITY )) { config_priority = OPT_VALUE_PRIORITY; config_priority_override = 1; priority_done = 0; } # endif # ifdef HAVE_WORKING_FORK /* make sure the FDs are initialised */ pipe_fds[0] = -1; pipe_fds[1] = -1; do { /* 'loop' once */ if (!HAVE_OPT( WAIT_SYNC )) break; wait_sync = OPT_VALUE_WAIT_SYNC; if (wait_sync <= 0) { wait_sync = 0; break; } /* -w requires a fork() even with debug > 0 */ nofork = FALSE; if (pipe(pipe_fds)) { exit_code = (errno) ? errno : -1; msyslog(LOG_ERR, "Pipe creation failed for --wait-sync: %m"); exit(exit_code); } waitsync_fd_to_close = pipe_fds[1]; } while (0); /* 'loop' once */ # endif /* HAVE_WORKING_FORK */ init_lib(); # ifdef SYS_WINNT /* * Start interpolation thread, must occur before first * get_systime() */ init_winnt_time(); # endif /* * Initialize random generator and public key pair */ get_systime(&now); ntp_srandom((int)(now.l_i * now.l_uf)); /* * Detach us from the terminal. May need an #ifndef GIZMO. */ if (!nofork) { # ifdef HAVE_WORKING_FORK rc = fork(); if (-1 == rc) { exit_code = (errno) ? errno : -1; msyslog(LOG_ERR, "fork: %m"); exit(exit_code); } if (rc > 0) { /* parent */ exit_code = wait_child_sync_if(pipe_fds[0], wait_sync); exit(exit_code); } /* * child/daemon * close all open files excepting waitsync_fd_to_close. * msyslog() unreliable until after init_logging(). */ closelog(); if (syslog_file != NULL) { fclose(syslog_file); syslog_file = NULL; syslogit = TRUE; } close_all_except(waitsync_fd_to_close); INSIST(0 == open("/dev/null", 0) && 1 == dup2(0, 1) \ && 2 == dup2(0, 2)); init_logging(progname, 0, TRUE); /* we lost our logfile (if any) daemonizing */ setup_logfile(logfilename); # ifdef SYS_DOMAINOS { uid_$t puid; status_$t st; proc2_$who_am_i(&puid); proc2_$make_server(&puid, &st); } # endif /* SYS_DOMAINOS */ # ifdef HAVE_SETSID if (setsid() == (pid_t)-1) msyslog(LOG_ERR, "setsid(): %m"); # elif defined(HAVE_SETPGID) if (setpgid(0, 0) == -1) msyslog(LOG_ERR, "setpgid(): %m"); # else /* !HAVE_SETSID && !HAVE_SETPGID follows */ # ifdef TIOCNOTTY fid = open("/dev/tty", 2); if (fid >= 0) { ioctl(fid, (u_long)TIOCNOTTY, NULL); close(fid); } # endif /* TIOCNOTTY */ ntp_setpgrp(0, getpid()); # endif /* !HAVE_SETSID && !HAVE_SETPGID */ # ifdef _AIX /* Don't get killed by low-on-memory signal. */ sa.sa_handler = catch_danger; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGDANGER, &sa, NULL); # endif /* _AIX */ # endif /* HAVE_WORKING_FORK */ } # ifdef SCO5_CLOCK /* * SCO OpenServer's system clock offers much more precise timekeeping * on the base CPU than the other CPUs (for multiprocessor systems), * so we must lock to the base CPU. */ fd = open("/dev/at1", O_RDONLY); if (fd >= 0) { zero = 0; if (ioctl(fd, ACPU_LOCK, &zero) < 0) msyslog(LOG_ERR, "cannot lock to base CPU: %m"); close(fd); } # endif /* Setup stack size in preparation for locking pages in memory. */ # if defined(HAVE_MLOCKALL) # ifdef HAVE_SETRLIMIT ntp_rlimit(RLIMIT_STACK, DFLT_RLIMIT_STACK * 4096, 4096, "4k"); # ifdef RLIMIT_MEMLOCK /* * The default RLIMIT_MEMLOCK is very low on Linux systems. * Unless we increase this limit malloc calls are likely to * fail if we drop root privilege. To be useful the value * has to be larger than the largest ntpd resident set size. */ ntp_rlimit(RLIMIT_MEMLOCK, DFLT_RLIMIT_MEMLOCK * 1024 * 1024, 1024 * 1024, "MB"); # endif /* RLIMIT_MEMLOCK */ # endif /* HAVE_SETRLIMIT */ # else /* !HAVE_MLOCKALL follows */ # ifdef HAVE_PLOCK # ifdef PROCLOCK # ifdef _AIX /* * set the stack limit for AIX for plock(). * see get_aix_stack() for more info. */ if (ulimit(SET_STACKLIM, (get_aix_stack() - 8 * 4096)) < 0) msyslog(LOG_ERR, "Cannot adjust stack limit for plock: %m"); # endif /* _AIX */ # endif /* PROCLOCK */ # endif /* HAVE_PLOCK */ # endif /* !HAVE_MLOCKALL */ /* * Set up signals we pay attention to locally. */ # ifdef SIGDIE1 signal_no_reset(SIGDIE1, finish); signal_no_reset(SIGDIE2, finish); signal_no_reset(SIGDIE3, finish); signal_no_reset(SIGDIE4, finish); # endif # ifdef SIGBUS signal_no_reset(SIGBUS, finish); # endif # if !defined(SYS_WINNT) && !defined(VMS) # ifdef DEBUG (void) signal_no_reset(MOREDEBUGSIG, moredebug); (void) signal_no_reset(LESSDEBUGSIG, lessdebug); # else (void) signal_no_reset(MOREDEBUGSIG, no_debug); (void) signal_no_reset(LESSDEBUGSIG, no_debug); # endif /* DEBUG */ # endif /* !SYS_WINNT && !VMS */ /* * Set up signals we should never pay attention to. */ # ifdef SIGPIPE signal_no_reset(SIGPIPE, SIG_IGN); # endif /* * Call the init_ routines to initialize the data structures. * * Exactly what command-line options are we expecting here? */ INIT_SSL(); init_auth(); init_util(); init_restrict(); init_mon(); init_timer(); init_request(); init_control(); init_peer(); # ifdef REFCLOCK init_refclock(); # endif set_process_priority(); init_proto(); /* Call at high priority */ init_io(); init_loopfilter(); mon_start(MON_ON); /* monitor on by default now */ /* turn off in config if unwanted */ /* * Get the configuration. This is done in a separate module * since this will definitely be different for the gizmo board. */ getconfig(argc, argv); if (-1 == cur_memlock) { # if defined(HAVE_MLOCKALL) /* * lock the process into memory */ if ( !HAVE_OPT(SAVECONFIGQUIT) # ifdef RLIMIT_MEMLOCK && -1 != DFLT_RLIMIT_MEMLOCK # endif && 0 != mlockall(MCL_CURRENT|MCL_FUTURE)) msyslog(LOG_ERR, "mlockall(): %m"); # else /* !HAVE_MLOCKALL follows */ # ifdef HAVE_PLOCK # ifdef PROCLOCK /* * lock the process into memory */ if (!HAVE_OPT(SAVECONFIGQUIT) && 0 != plock(PROCLOCK)) msyslog(LOG_ERR, "plock(PROCLOCK): %m"); # else /* !PROCLOCK follows */ # ifdef TXTLOCK /* * Lock text into ram */ if (!HAVE_OPT(SAVECONFIGQUIT) && 0 != plock(TXTLOCK)) msyslog(LOG_ERR, "plock(TXTLOCK) error: %m"); # else /* !TXTLOCK follows */ msyslog(LOG_ERR, "plock() - don't know what to lock!"); # endif /* !TXTLOCK */ # endif /* !PROCLOCK */ # endif /* HAVE_PLOCK */ # endif /* !HAVE_MLOCKALL */ } loop_config(LOOP_DRIFTINIT, 0); report_event(EVNT_SYSRESTART, NULL, NULL); initializing = FALSE; # ifdef HAVE_DROPROOT if (droproot) { /* Drop super-user privileges and chroot now if the OS supports this */ # ifdef HAVE_LINUX_CAPABILITIES /* set flag: keep privileges accross setuid() call (we only really need cap_sys_time): */ if (prctl( PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L ) == -1) { msyslog( LOG_ERR, "prctl( PR_SET_KEEPCAPS, 1L ) failed: %m" ); exit(-1); } # elif HAVE_SOLARIS_PRIVS /* Nothing to do here */ # else /* we need a user to switch to */ if (user == NULL) { msyslog(LOG_ERR, "Need user name to drop root privileges (see -u flag!)" ); exit(-1); } # endif /* HAVE_LINUX_CAPABILITIES || HAVE_SOLARIS_PRIVS */ if (user != NULL) { if (isdigit((unsigned char)*user)) { sw_uid = (uid_t)strtoul(user, &endp, 0); if (*endp != '\0') goto getuser; if ((pw = getpwuid(sw_uid)) != NULL) { free(user); user = estrdup(pw->pw_name); sw_gid = pw->pw_gid; } else { errno = 0; msyslog(LOG_ERR, "Cannot find user ID %s", user); exit (-1); } } else { getuser: errno = 0; if ((pw = getpwnam(user)) != NULL) { sw_uid = pw->pw_uid; sw_gid = pw->pw_gid; } else { if (errno) msyslog(LOG_ERR, "getpwnam(%s) failed: %m", user); else msyslog(LOG_ERR, "Cannot find user `%s'", user); exit (-1); } } } if (group != NULL) { if (isdigit((unsigned char)*group)) { sw_gid = (gid_t)strtoul(group, &endp, 0); if (*endp != '\0') goto getgroup; } else { getgroup: if ((gr = getgrnam(group)) != NULL) { sw_gid = gr->gr_gid; } else { errno = 0; msyslog(LOG_ERR, "Cannot find group `%s'", group); exit (-1); } } } if (chrootdir ) { /* make sure cwd is inside the jail: */ if (chdir(chrootdir)) { msyslog(LOG_ERR, "Cannot chdir() to `%s': %m", chrootdir); exit (-1); } if (chroot(chrootdir)) { msyslog(LOG_ERR, "Cannot chroot() to `%s': %m", chrootdir); exit (-1); } if (chdir("/")) { msyslog(LOG_ERR, "Cannot chdir() to`root after chroot(): %m"); exit (-1); } } # ifdef HAVE_SOLARIS_PRIVS if ((lowprivs = priv_str_to_set(LOWPRIVS, ",", NULL)) == NULL) { msyslog(LOG_ERR, "priv_str_to_set() failed:%m"); exit(-1); } if ((highprivs = priv_allocset()) == NULL) { msyslog(LOG_ERR, "priv_allocset() failed:%m"); exit(-1); } (void) getppriv(PRIV_PERMITTED, highprivs); (void) priv_intersect(highprivs, lowprivs); if (setppriv(PRIV_SET, PRIV_PERMITTED, lowprivs) == -1) { msyslog(LOG_ERR, "setppriv() failed:%m"); exit(-1); } # endif /* HAVE_SOLARIS_PRIVS */ if (user && initgroups(user, sw_gid)) { msyslog(LOG_ERR, "Cannot initgroups() to user `%s': %m", user); exit (-1); } if (group && setgid(sw_gid)) { msyslog(LOG_ERR, "Cannot setgid() to group `%s': %m", group); exit (-1); } if (group && setegid(sw_gid)) { msyslog(LOG_ERR, "Cannot setegid() to group `%s': %m", group); exit (-1); } if (group) { if (0 != setgroups(1, &sw_gid)) { msyslog(LOG_ERR, "setgroups(1, %d) failed: %m", sw_gid); exit (-1); } } else if (pw) if (0 != initgroups(pw->pw_name, pw->pw_gid)) { msyslog(LOG_ERR, "initgroups(<%s>, %d) filed: %m", pw->pw_name, pw->pw_gid); exit (-1); } if (user && setuid(sw_uid)) { msyslog(LOG_ERR, "Cannot setuid() to user `%s': %m", user); exit (-1); } if (user && seteuid(sw_uid)) { msyslog(LOG_ERR, "Cannot seteuid() to user `%s': %m", user); exit (-1); } # if !defined(HAVE_LINUX_CAPABILITIES) && !defined(HAVE_SOLARIS_PRIVS) /* * for now assume that the privilege to bind to privileged ports * is associated with running with uid 0 - should be refined on * ports that allow binding to NTP_PORT with uid != 0 */ disable_dynamic_updates |= (sw_uid != 0); /* also notifies routing message listener */ # endif /* !HAVE_LINUX_CAPABILITIES && !HAVE_SOLARIS_PRIVS */ if (disable_dynamic_updates && interface_interval) { interface_interval = 0; msyslog(LOG_INFO, "running as non-root disables dynamic interface tracking"); } # ifdef HAVE_LINUX_CAPABILITIES { /* * We may be running under non-root uid now, but we still hold full root privileges! * We drop all of them, except for the crucial one or two: cap_sys_time and * cap_net_bind_service if doing dynamic interface tracking. */ cap_t caps; char *captext; captext = (0 != interface_interval) ? "cap_sys_time,cap_net_bind_service=pe" : "cap_sys_time=pe"; caps = cap_from_text(captext); if (!caps) { msyslog(LOG_ERR, "cap_from_text(%s) failed: %m", captext); exit(-1); } if (-1 == cap_set_proc(caps)) { msyslog(LOG_ERR, "cap_set_proc() failed to drop root privs: %m"); exit(-1); } cap_free(caps); } # endif /* HAVE_LINUX_CAPABILITIES */ # ifdef HAVE_SOLARIS_PRIVS if (priv_delset(lowprivs, "proc_setid") == -1) { msyslog(LOG_ERR, "priv_delset() failed:%m"); exit(-1); } if (setppriv(PRIV_SET, PRIV_PERMITTED, lowprivs) == -1) { msyslog(LOG_ERR, "setppriv() failed:%m"); exit(-1); } priv_freeset(lowprivs); priv_freeset(highprivs); # endif /* HAVE_SOLARIS_PRIVS */ root_dropped = TRUE; fork_deferred_worker(); } /* if (droproot) */ # endif /* HAVE_DROPROOT */ /* libssecomp sandboxing */ #if defined (LIBSECCOMP) && (KERN_SECCOMP) scmp_filter_ctx ctx; if ((ctx = seccomp_init(SCMP_ACT_KILL)) < 0) msyslog(LOG_ERR, "%s: seccomp_init(SCMP_ACT_KILL) failed: %m", __func__); else { msyslog(LOG_DEBUG, "%s: seccomp_init(SCMP_ACT_KILL) succeeded", __func__); } #ifdef __x86_64__ int scmp_sc[] = { SCMP_SYS(adjtimex), SCMP_SYS(bind), SCMP_SYS(brk), SCMP_SYS(chdir), SCMP_SYS(clock_gettime), SCMP_SYS(clock_settime), SCMP_SYS(close), SCMP_SYS(connect), SCMP_SYS(exit_group), SCMP_SYS(fstat), SCMP_SYS(fsync), SCMP_SYS(futex), SCMP_SYS(getitimer), SCMP_SYS(getsockname), SCMP_SYS(ioctl), SCMP_SYS(lseek), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(munmap), SCMP_SYS(open), SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(recvmsg), SCMP_SYS(rename), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigreturn), SCMP_SYS(select), SCMP_SYS(sendto), SCMP_SYS(setitimer), SCMP_SYS(setsid), SCMP_SYS(socket), SCMP_SYS(stat), SCMP_SYS(time), SCMP_SYS(write), }; #endif #ifdef __i386__ int scmp_sc[] = { SCMP_SYS(_newselect), SCMP_SYS(adjtimex), SCMP_SYS(brk), SCMP_SYS(chdir), SCMP_SYS(clock_gettime), SCMP_SYS(clock_settime), SCMP_SYS(close), SCMP_SYS(exit_group), SCMP_SYS(fsync), SCMP_SYS(futex), SCMP_SYS(getitimer), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2), SCMP_SYS(munmap), SCMP_SYS(open), SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(rename), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(select), SCMP_SYS(setitimer), SCMP_SYS(setsid), SCMP_SYS(sigprocmask), SCMP_SYS(sigreturn), SCMP_SYS(socketcall), SCMP_SYS(stat64), SCMP_SYS(time), SCMP_SYS(write), }; #endif { int i; for (i = 0; i < COUNTOF(scmp_sc); i++) { if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, scmp_sc[i], 0) < 0) { msyslog(LOG_ERR, "%s: seccomp_rule_add() failed: %m", __func__); } } } if (seccomp_load(ctx) < 0) msyslog(LOG_ERR, "%s: seccomp_load() failed: %m", __func__); else { msyslog(LOG_DEBUG, "%s: seccomp_load() succeeded", __func__); } #endif /* LIBSECCOMP and KERN_SECCOMP */ # ifdef HAVE_IO_COMPLETION_PORT for (;;) { GetReceivedBuffers(); # else /* normal I/O */ BLOCK_IO_AND_ALARM(); was_alarmed = FALSE; for (;;) { if (alarm_flag) { /* alarmed? */ was_alarmed = TRUE; alarm_flag = FALSE; } if (!was_alarmed && !has_full_recv_buffer()) { /* * Nothing to do. Wait for something. */ io_handler(); } if (alarm_flag) { /* alarmed? */ was_alarmed = TRUE; alarm_flag = FALSE; } if (was_alarmed) { UNBLOCK_IO_AND_ALARM(); /* * Out here, signals are unblocked. Call timer routine * to process expiry. */ timer(); was_alarmed = FALSE; BLOCK_IO_AND_ALARM(); } # endif /* !HAVE_IO_COMPLETION_PORT */ # ifdef DEBUG_TIMING { l_fp pts; l_fp tsa, tsb; int bufcount = 0; get_systime(&pts); tsa = pts; # endif rbuf = get_full_recv_buffer(); while (rbuf != NULL) { if (alarm_flag) { was_alarmed = TRUE; alarm_flag = FALSE; } UNBLOCK_IO_AND_ALARM(); if (was_alarmed) { /* avoid timer starvation during lengthy I/O handling */ timer(); was_alarmed = FALSE; } /* * Call the data procedure to handle each received * packet. */ if (rbuf->receiver != NULL) { # ifdef DEBUG_TIMING l_fp dts = pts; L_SUB(&dts, &rbuf->recv_time); DPRINTF(2, ("processing timestamp delta %s (with prec. fuzz)\n", lfptoa(&dts, 9))); collect_timing(rbuf, "buffer processing delay", 1, &dts); bufcount++; # endif (*rbuf->receiver)(rbuf); } else { msyslog(LOG_ERR, "fatal: receive buffer callback NULL"); abort(); } BLOCK_IO_AND_ALARM(); freerecvbuf(rbuf); rbuf = get_full_recv_buffer(); } # ifdef DEBUG_TIMING get_systime(&tsb); L_SUB(&tsb, &tsa); if (bufcount) { collect_timing(NULL, "processing", bufcount, &tsb); DPRINTF(2, ("processing time for %d buffers %s\n", bufcount, lfptoa(&tsb, 9))); } } # endif /* * Go around again */ # ifdef HAVE_DNSREGISTRATION if (mdnsreg && (current_time - mdnsreg ) > 60 && mdnstries && sys_leap != LEAP_NOTINSYNC) { mdnsreg = current_time; msyslog(LOG_INFO, "Attempting to register mDNS"); if ( DNSServiceRegister (&mdns, 0, 0, NULL, "_ntp._udp", NULL, NULL, htons(NTP_PORT), 0, NULL, NULL, NULL) != kDNSServiceErr_NoError ) { if (!--mdnstries) { msyslog(LOG_ERR, "Unable to register mDNS, giving up."); } else { msyslog(LOG_INFO, "Unable to register mDNS, will try later."); } } else { msyslog(LOG_INFO, "mDNS service registered."); mdnsreg = FALSE; } } # endif /* HAVE_DNSREGISTRATION */ } UNBLOCK_IO_AND_ALARM(); return 1; } #endif /* !SIM */ #if !defined(SIM) && defined(SIGDIE1) /* * finish - exit gracefully */ static RETSIGTYPE finish( int sig ) { const char *sig_desc; sig_desc = NULL; #ifdef HAVE_STRSIGNAL sig_desc = strsignal(sig); #endif if (sig_desc == NULL) sig_desc = ""; msyslog(LOG_NOTICE, "%s exiting on signal %d (%s)", progname, sig, sig_desc); /* See Bug 2513 and Bug 2522 re the unlink of PIDFILE */ # ifdef HAVE_DNSREGISTRATION if (mdns != NULL) DNSServiceRefDeallocate(mdns); # endif peer_cleanup(); exit(0); } #endif /* !SIM && SIGDIE1 */ #ifndef SIM /* * wait_child_sync_if - implements parent side of -w/--wait-sync */ # ifdef HAVE_WORKING_FORK static int wait_child_sync_if( int pipe_read_fd, long wait_sync ) { int rc; int exit_code; time_t wait_end_time; time_t cur_time; time_t wait_rem; fd_set readset; struct timeval wtimeout; if (0 == wait_sync) return 0; /* waitsync_fd_to_close used solely by child */ close(waitsync_fd_to_close); wait_end_time = time(NULL) + wait_sync; do { cur_time = time(NULL); wait_rem = (wait_end_time > cur_time) ? (wait_end_time - cur_time) : 0; wtimeout.tv_sec = wait_rem; wtimeout.tv_usec = 0; FD_ZERO(&readset); FD_SET(pipe_read_fd, &readset); rc = select(pipe_read_fd + 1, &readset, NULL, NULL, &wtimeout); if (-1 == rc) { if (EINTR == errno) continue; exit_code = (errno) ? errno : -1; msyslog(LOG_ERR, "--wait-sync select failed: %m"); return exit_code; } if (0 == rc) { /* * select() indicated a timeout, but in case * its timeouts are affected by a step of the * system clock, select() again with a zero * timeout to confirm. */ FD_ZERO(&readset); FD_SET(pipe_read_fd, &readset); wtimeout.tv_sec = 0; wtimeout.tv_usec = 0; rc = select(pipe_read_fd + 1, &readset, NULL, NULL, &wtimeout); if (0 == rc) /* select() timeout */ break; else /* readable */ return 0; } else /* readable */ return 0; } while (wait_rem > 0); fprintf(stderr, "%s: -w/--wait-sync %ld timed out.\n", progname, wait_sync); return ETIMEDOUT; }
/** * Grabs extra non-root capabilities / privileges that we might require. * * This is currently only used for being able to do ICMP from the NAT engine. * * @note We still have root privileges at the time of this call. */ static void supR3HardenedMainGrabCapabilites(void) { # if defined(RT_OS_LINUX) /* * We are about to drop all our privileges. Remove all capabilities but * keep the cap_net_raw capability for ICMP sockets for the NAT stack. */ if (g_uCaps != 0) { # ifdef USE_LIB_PCAP /* XXX cap_net_bind_service */ if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep"))) prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0); prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0); # else cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr)); cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap)); memset(hdr, 0, sizeof(*hdr)); hdr->version = _LINUX_CAPABILITY_VERSION; memset(cap, 0, sizeof(*cap)); cap->effective = g_uCaps; cap->permitted = g_uCaps; if (!capset(hdr, cap)) prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0); prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0); # endif /* !USE_LIB_PCAP */ } # elif defined(RT_OS_SOLARIS) /* * Add net_icmpaccess privilege to effective privileges and limit * permitted privileges before completely dropping root privileges. * This requires dropping root privileges temporarily to get the normal * user's privileges. */ seteuid(g_uid); priv_set_t *pPrivEffective = priv_allocset(); priv_set_t *pPrivNew = priv_allocset(); if (pPrivEffective && pPrivNew) { int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective); seteuid(0); if (!rc) { priv_copyset(pPrivEffective, pPrivNew); rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS); if (!rc) { /* Order is important, as one can't set a privilege which is * not in the permitted privilege set. */ rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew); if (rc) supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n"); rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew); if (rc) supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n"); } else supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n"); } } else { /* for memory allocation failures just continue */ seteuid(0); } if (pPrivEffective) priv_freeset(pPrivEffective); if (pPrivNew) priv_freeset(pPrivNew); # endif }
void set_gssd_uid(uid_t uid) { /* Initialize */ if (lowprivs == NULL) { /* L, P & I shall not change in gssd; we manipulate P though */ if ((highprivs = priv_allocset()) == NULL || (lowprivs = priv_str_to_set(LOWPRIVS, ",", NULL)) == NULL) { printf(gettext( "fatal: can't allocate privilege set (%s)\n"), strerror(ENOMEM)); syslog(LOG_ERR, "Fatal: can't allocate privilege " "set (%s)"), strerror(ENOMEM); exit(1); } /* P has the privs we need when we need privs */ (void) getppriv(PRIV_PERMITTED, highprivs); /* * In case "basic" grows privs not excluded in LOWPRIVS * but excluded in the service's method_context */ priv_intersect(highprivs, lowprivs); (void) setpflags(PRIV_AWARE, 1); } printf(gettext("set_gssd_uid called with uid = %d\n"), uid); /* * nfsd runs as UID 1, so upcalls triggered by nfsd will cause uid to * 1 here, but nfsd's upcalls need to run as root with privs here. */ if (uid == 1) uid = 0; /* * Set the value of krb5_cc_uid, so it can be retrieved when * app_krb5_user_uid() is called by the underlying mechanism * libraries. This should go away soon. */ krb5_cc_uid = uid; /* Claw privs back */ (void) setppriv(PRIV_SET, PRIV_EFFECTIVE, highprivs); /* * Switch uid and set the saved set-uid to 0 so setuid(0) will work * later. */ if (setuid(0) != 0 || (uid != 0 && setreuid(uid, -1) != 0) || (uid != 0 && seteuid(uid) != 0)) { /* Not enough privs, so bail! */ printf(gettext( "fatal: gssd is running with insufficient privilege\n")); syslog(LOG_ERR, "Fatal: gssd is running with insufficient " "privilege."); exit(1); } /* Temporarily drop privs, but only if uid != 0 */ if (uid != 0) (void) setppriv(PRIV_SET, PRIV_EFFECTIVE, lowprivs); }
int _zexec(const char *a_zoneName, const char *a_path, char *a_argv[]) { zoneid_t zoneid; zone_state_t st; char **new_env = { NULL }; priv_set_t *privset; /* entry assertions */ assert(a_zoneName != NULL); assert(*a_zoneName != '\0'); assert(a_path != NULL); assert(*a_path != '\0'); /* establish locale settings */ (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* can only be invoked from within the global zone */ if (getzoneid() != GLOBAL_ZONEID) { _z_program_error(ERR_ZEXEC_NOT_IN_GZ, a_zoneName); return (-1); } if (strcmp(a_zoneName, GLOBAL_ZONENAME) == 0) { _z_program_error(ERR_ZEXEC_GZUSED, a_zoneName); return (-1); } /* get the state of the specified zone */ if (zone_get_state((char *)a_zoneName, &st) != Z_OK) { _z_program_error(ERR_ZEXEC_BADZONE, a_zoneName); return (-1); } if (st < ZONE_STATE_INSTALLED) { _z_program_error(ERR_ZEXEC_BADSTATE, a_zoneName, zone_state_str(st)); return (-1); } if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) { _z_program_error(ERR_ZEXEC_NOTRUNNING, a_zoneName, zone_state_str(st)); return (-1); } /* * In both console and non-console cases, we require all privs. * In the console case, because we may need to startup zoneadmd. * In the non-console case in order to do zone_enter(2), zonept() * and other tasks. * * Future work: this solution is temporary. Ultimately, we need to * move to a flexible system which allows the global admin to * designate that a particular user can zlogin (and probably zlogin * -C) to a particular zone. This all-root business we have now is * quite sketchy. */ if ((privset = priv_allocset()) == NULL) { _z_program_error(ERR_ZEXEC_PRIV_ALLOCSET, a_zoneName, strerror(errno)); return (-1); } if (getppriv(PRIV_EFFECTIVE, privset) != 0) { _z_program_error(ERR_ZEXEC_GETPPRIV, a_zoneName, strerror(errno)); priv_freeset(privset); return (-1); } if (priv_isfullset(privset) == B_FALSE) { _z_program_error(ERR_ZEXEC_PRIVS, a_zoneName); priv_freeset(privset); return (-1); } priv_freeset(privset); if ((zoneid = getzoneidbyname(a_zoneName)) == -1) { _z_program_error(ERR_ZEXEC_NOZONEID, a_zoneName, strerror(errno)); return (-1); } if ((new_env = _zexec_prep_env()) == NULL) { _z_program_error(ERR_ZEXEC_ASSEMBLE, a_zoneName); return (-1); } /* * In case any of stdin, stdout or stderr are streams, * anchor them to prevent malicious I_POPs. * * Future work: use pipes to entirely eliminate FD leakage * into the zone. */ (void) ioctl(STDIN_FILENO, I_ANCHOR); (void) ioctl(STDOUT_FILENO, I_ANCHOR); (void) ioctl(STDERR_FILENO, I_ANCHOR); if (zone_enter(zoneid) == -1) { int lerrno = errno; _z_program_error(ERR_ZEXEC_ZONEENTER, a_zoneName, strerror(errno)); if (lerrno == EFAULT) { _z_program_error(ERR_ZEXEC_EFAULT, a_zoneName); } free(new_env); return (-1); } (void) execve(a_path, &a_argv[0], new_env); _z_program_error(ERR_ZEXEC_EXECFAILURE, a_zoneName, strerror(errno)); return (-1); }
/* Specify what privileges an suid root binary needs. */ int __init_suid_priv (int flags, ...) { int res = 0; priv_set_t *permit = NULL, *inherit = NULL, *scratch = NULL; /* Check flags. */ if (flags != PU_LIMITPRIVS && flags != PU_CLEARLIMITSET) return -1; /* We can only initialize once. */ if (__suidset) return -1; /* Do nothing if we are running as root but not setuid root. */ uid_t uid = getuid (); uid_t euid = geteuid (); if (uid == 0 && euid == 0) return 0; /* Allocate a scratch set. */ scratch = priv_allocset (); if (!scratch) goto error; /* Get the basic set. */ const priv_data_t *pd = __priv_parse_data_cached (); if (!pd) goto error; priv_set_t *basic = pd->pd_basicprivs; /* Get the inherited set. */ inherit = priv_allocset (); if (!inherit) goto error; if (getppriv (PRIV_INHERITABLE, inherit) != 0) goto error; /* Get the permitted set. */ permit = priv_allocset (); if (!permit) goto error; if (getppriv (PRIV_PERMITTED, permit) != 0) goto error; /* Get passed privileges. */ __suidset = priv_allocset (); if (!__suidset) goto error; priv_emptyset (__suidset); va_list ap; va_start (ap, flags); const char *priv; while ((priv = va_arg (ap, const char *))) if (priv_addset (__suidset, priv) != 0) goto error; /* Make sure that the passed privileges are a subset of the current permitted privileges. */ if (priv_issubset (__suidset, permit) != _B_TRUE) goto error; /* Set the effective privileges to the inherited ones. */ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, inherit) != 0) goto error; /* Set the permitted privileges to those currently permitted privileges in set of the ones passed in, the inherited ones, and the basic set. */ priv_copyset (__suidset, scratch); priv_union (inherit, scratch); if (basic) priv_union (basic, scratch); priv_intersect (permit, scratch); if (setppriv (PRIV_SET, PRIV_PERMITTED, scratch) != 0) goto error; /* Check if we need to set the limit set. */ if (flags & PU_CLEARLIMITSET) { priv_emptyset (scratch); if (setppriv (PRIV_SET, PRIV_LIMIT, scratch) != 0) goto error; } else if (flags & PU_LIMITPRIVS) { if (setppriv (PRIV_SET, PRIV_LIMIT, scratch) != 0) goto error; } /* Change the uid to the caller's uid if we're setuid root. */ if (euid == 0 && setreuid (uid, uid) != 0) goto error; goto out; error: res = -1; if (__suidset) { priv_freeset (__suidset); __suidset = NULL; } if (euid == 0) setreuid (uid, uid); out: priv_freeset (permit); priv_freeset (inherit); priv_freeset (scratch); return res; }
int mtev_security_usergroup(const char *user, const char *group, mtev_boolean effective) { static long pwnam_buflen = 0; static long grnam_buflen = 0; uid_t uid = 0; gid_t gid = 0; if(pwnam_buflen == 0) #ifdef _SC_GETPW_R_SIZE_MAX pwnam_buflen = sysconf(_SC_GETPW_R_SIZE_MAX); #else pwnam_buflen = 100; /* This shouldn't be used, so size is not important. */ #endif if(grnam_buflen == 0) #ifdef _SC_GETGR_R_SIZE_MAX grnam_buflen = sysconf(_SC_GETGR_R_SIZE_MAX); #else grnam_buflen = 100; #endif if(user) { if(isuinteger(user)) uid = atoi(user); else { struct passwd *pw, _pw; char *buf; if(NULL == (buf = alloca(pwnam_buflen))) BAIL("alloca failed\n"); if(NULL == (pw = __getpwnam_r(user, &_pw, buf, pwnam_buflen))) BAIL("Cannot find user '%s'\n", user); uid = pw->pw_uid; } } if(group) { if(isuinteger(group)) gid = atoi(group); else { struct group *gr, _gr; char *buf; if(NULL == (buf = alloca(grnam_buflen))) BAIL("alloca failed\n"); if(NULL == (gr = __getgrnam_r(group, &_gr, buf, grnam_buflen))) BAIL("Cannot find group '%s'\n", group); gid = gr->gr_gid; } } if(!user && !group) return 0; #if defined(CAP_SUPPORTED) && defined(HAVE_SETPPRIV) if(!effective && getpflags(PRIV_AWARE)) { int rv; priv_set_t *set; set = priv_allocset(); priv_addset(set, "proc_setid"); rv = setppriv(PRIV_ON, PRIV_EFFECTIVE, set); priv_freeset(set); if(rv) BAIL("setppriv(proc_setid) failed"); } #endif if(group) { if(!effective && gid == 0) BAIL("Cannot use this function to setgid(0)\n"); if((effective ? setegid(gid) : setgid(gid)) != 0) BAIL("setgid(%d) failed: %s\n", (int)gid, strerror(errno)); } if(user) { if(!effective && uid == 0) BAIL("Cannot use this function to setuid(0)\n"); if((effective ? seteuid(uid) : setuid(uid)) != 0) BAIL("setgid(%d) failed: %s\n", (int)gid, strerror(errno)); if(!effective) { #if defined(CAP_SUPPORTED) && defined(HAVE_SETPPRIV) if(getpflags(PRIV_AWARE)) { int rv; priv_set_t *set; set = priv_allocset(); priv_addset(set, "proc_setid"); rv = setppriv(PRIV_OFF, PRIV_EFFECTIVE, set); if(rv) BAIL("setppriv(off, effective, proc_setid) failed"); getppriv(PRIV_EFFECTIVE, set); rv = setppriv(PRIV_SET, PRIV_PERMITTED, set); if(rv) BAIL("setppriv(effective -> permitted) failed"); priv_freeset(set); } #endif if(setuid(0) == 0) BAIL("setuid(0) worked, and it shouldn't have.\n"); if(setgid(0) == 0) BAIL("setgid(0) worked, and it shouldn't have.\n"); } } return 0; }