void MGT_Run(void) { struct sigaction sac; struct vev *e; int i; mgt_uptime_t0 = VTIM_real(); e = vev_new(); XXXAN(e); e->callback = mgt_uptime; e->timeout = 1.0; e->name = "mgt_uptime"; AZ(vev_add(mgt_evb, e)); e = vev_new(); XXXAN(e); e->sig = SIGTERM; e->callback = mgt_sigint; e->name = "mgt_sigterm"; AZ(vev_add(mgt_evb, e)); e = vev_new(); XXXAN(e); e->sig = SIGINT; e->callback = mgt_sigint; e->name = "mgt_sigint"; AZ(vev_add(mgt_evb, e)); #ifdef HAVE_SETPROCTITLE setproctitle("Varnish-Mgr %s", heritage.name); #endif memset(&sac, 0, sizeof sac); sac.sa_handler = SIG_IGN; sac.sa_flags = SA_RESTART; AZ(sigaction(SIGPIPE, &sac, NULL)); AZ(sigaction(SIGHUP, &sac, NULL)); if (!d_flag && !mgt_has_vcl()) MGT_complain(C_ERR, "No VCL loaded yet"); else if (!d_flag) { mgt_launch_child(NULL); if (child_state != CH_RUNNING) { // XXX correct? or 0? exit_status = 2; return; } } mgt_SHM_Commit(); i = vev_schedule(mgt_evb); if (i != 0) MGT_complain(C_ERR, "vev_schedule() = %d", i); MGT_complain(C_INFO, "manager dies"); }
void mgt_sandbox_init(void) { struct passwd *pwd; struct group *grp; struct vsb *sb; unsigned subs; /* Pick a sandbox */ #ifdef HAVE_SETPPRIV mgt_sandbox = mgt_sandbox_solaris; #else mgt_sandbox = mgt_sandbox_unix; #endif /* Test it */ sb = VSB_new_auto(); subs = VSUB_run(sb, run_sandbox_test, NULL, "SANDBOX-test", 10); VSB_delete(sb); if (subs) { MGT_complain(C_SECURITY, "Platform-specific sandbox failed - sandboxing disabled"); MGT_complain(C_SECURITY, "Varnish runs with elevated privileges"); mgt_sandbox = mgt_sandbox_null; } MCF_AddParams(mgt_parspec_sandbox); /* * If we have nobody/nogroup, use them as defaults for sandboxes, * else fall back to whoever we run as. */ if (getpwnam("nobody") != NULL) { MCF_SetDefault("user", "nobody"); } else { pwd = getpwuid(getuid()); if (pwd == NULL) ARGV_ERR("Neither user 'nobody' or my uid (%jd)" " found in password database.\n", (intmax_t)getuid()); MCF_SetDefault("user", pwd->pw_name); } endpwent(); if (getgrnam("nogroup") != NULL) { MCF_SetDefault("group", "nogroup"); } else { grp = getgrgid(getgid()); if (grp == NULL) ARGV_ERR("Neither group 'nogroup' or my gid (%jd)" " found in password database.\n", (intmax_t)getgid()); MCF_SetDefault("group", grp->gr_name); } endgrent(); }
static void mgt_cli_cb_after(const struct cli *cli) { MGT_complain(C_CLI, "CLI %s Wr %03u %s", cli->ident, cli->result, VSB_data(cli->sb)); }
Marg_connect(const struct vev *e, int what) { struct vsb *vsb; struct m_addr *ma; assert(e == M_conn); (void)what; M_fd = VTCP_connected(M_fd); if (M_fd < 0) { MGT_complain(C_INFO, "Could not connect to CLI-master: %m"); ma = VTAILQ_FIRST(&m_addr_list); AN(ma); VTAILQ_REMOVE(&m_addr_list, ma, list); VTAILQ_INSERT_TAIL(&m_addr_list, ma, list); if (M_poll < 10) M_poll++; return (1); } vsb = sock_id("master", M_fd); mgt_cli_setup(M_fd, M_fd, 0, VSB_data(vsb), Marg_closer, NULL); VSB_destroy(&vsb); M_poll = 1; return (1); }
static void vjs_setup(enum jail_gen_e jge) { priv_set_t *priv_all; if (! (priv_all = priv_allocset())) { MGT_complain(C_SECURITY, "Solaris Jail warning: " " vjs_setup - priv_allocset failed: errno=%d (%s)", errno, strerror(errno)); return; } priv_emptyset(priv_all); vjs_add_inheritable(priv_all, jge); vjs_add_effective(priv_all, jge); vjs_add_permitted(priv_all, jge); vjs_add_initial(priv_all, jge); /* try to get all possible privileges, expect EPERM here */ setppriv_assert(setppriv(PRIV_ON, PRIV_PERMITTED, priv_all)); setppriv_assert(setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_all)); setppriv_assert(setppriv(PRIV_ON, PRIV_INHERITABLE, priv_all)); priv_freeset(priv_all); }
static int child_line(void *priv, const char *p) { (void)priv; MGT_complain(C_INFO, "Child (%jd) said %s", (intmax_t)child_pid, p); return (0); }
mgt_sigint(const struct vev *e, int what) { (void)e; (void)what; MGT_complain(C_ERR, "Manager got SIGINT"); (void)fflush(stdout); if (child_pid >= 0) mgt_stop_child(); exit(0); }
static void vjs_waive(enum jail_gen_e jge) { priv_set_t *effective, *inheritable, *permitted, *limited; if (!(effective = priv_allocset()) || !(inheritable = priv_allocset()) || !(permitted = priv_allocset()) || !(limited = priv_allocset())) { MGT_complain(C_SECURITY, "Solaris Jail warning: " " vjs_waive - priv_allocset failed: errno=%d (%s)", errno, strerror(errno)); return; } /* * inheritable and effective are distinct sets * effective is a subset of permitted * limit is the union of all */ priv_emptyset(inheritable); vjs_add_inheritable(inheritable, jge); priv_emptyset(effective); vjs_add_effective(effective, jge); priv_copyset(effective, permitted); vjs_add_permitted(permitted, jge); priv_copyset(inheritable, limited); priv_union(permitted, limited); /* * invert the sets and clear privileges such that setppriv will always * succeed */ priv_inverse(limited); priv_inverse(permitted); priv_inverse(effective); priv_inverse(inheritable); AZ(setppriv(PRIV_OFF, PRIV_LIMIT, limited)); AZ(setppriv(PRIV_OFF, PRIV_PERMITTED, permitted)); AZ(setppriv(PRIV_OFF, PRIV_EFFECTIVE, effective)); AZ(setppriv(PRIV_OFF, PRIV_INHERITABLE, inheritable)); priv_freeset(limited); priv_freeset(permitted); priv_freeset(effective); priv_freeset(inheritable); }
void mgt_stop_child(void) { if (child_state != CH_RUNNING) return; child_state = CH_STOPPING; MGT_complain(C_DEBUG, "Stopping Child"); mgt_reap_child(); }
vju_make_vcldir(const char *dname) { AZ(seteuid(0)); if (mkdir(dname, 0755) < 0 && errno != EEXIST) { MGT_complain(C_ERR, "Cannot create VCL directory '%s': %s", dname, strerror(errno)); return (1); } AZ(chown(dname, vju_uid, vju_gid)); AZ(seteuid(vju_uid)); return (0); }
mgt_sandbox_unix(enum sandbox_e who) { #define NGID 2000 int i; gid_t gid, gid_list[NGID]; uid_t uid; if (who == SANDBOX_TESTING) { /* * Test if sandboxing is going to work. * Do not assert on failure here, but simply exit non-zero. */ gid = getgid(); gid += 1; if (setgid(gid)) exit(1); uid = getuid(); uid += 1; if (setuid(uid)) exit(2); exit(0); } /* * Do the real thing, assert if we fail */ AZ(setgid(mgt_param.gid)); AZ(initgroups(mgt_param.user, mgt_param.gid)); if (who == SANDBOX_CC && strlen(mgt_param.group_cc) > 0) { /* Add the optional extra group for the C-compiler access */ i = getgroups(NGID, gid_list); assert(i >= 0); gid_list[i++] = mgt_param.gid_cc; AZ(setgroups(i, gid_list)); } AZ(setuid(mgt_param.uid)); #ifdef __linux__ /* * On linux mucking about with uid/gid disables core-dumps, * reenable them again. */ if (prctl(PR_SET_DUMPABLE, 1) != 0) { MGT_complain(C_INFO, "Could not set dumpable bit. Core dumps turned off\n"); } #endif }
void MGT_Child_Cli_Fail(void) { if (child_state != CH_RUNNING) return; if (child_pid < 0) return; MGT_complain(C_ERR, "Child (%jd) not responding to CLI, killing it.", (intmax_t)child_pid); if (MGT_FEATURE(FEATURE_NO_COREDUMP)) (void)kill(child_pid, SIGKILL); else (void)kill(child_pid, SIGQUIT); }
static void vjs_privsep(enum jail_gen_e jge) { (void)jge; if (priv_ineffect(PRIV_PROC_SETID)) { if (getgid() != mgt_param.gid) XXXAZ(setgid(mgt_param.gid)); if (getuid() != mgt_param.uid) XXXAZ(setuid(mgt_param.uid)); } else { MGT_complain(C_SECURITY, "Privilege %s missing, will not change uid/gid", PRIV_PROC_SETID); } }
static void mgt_panic_record(pid_t r) { char time_str[30]; if (child_panic != NULL) VSB_delete(child_panic); child_panic = VSB_new_auto(); AN(child_panic); VTIM_format(VTIM_real(), time_str); VSB_printf(child_panic, "Last panic at: %s\n", time_str); VSB_quote(child_panic, heritage.panic_str, strnlen(heritage.panic_str, heritage.panic_str_len), VSB_QUOTE_NONL); AZ(VSB_finish(child_panic)); MGT_complain(C_ERR, "Child (%jd) %s", (intmax_t)r, VSB_data(child_panic)); }
vju_subproc(enum jail_subproc_e jse) { int i; gid_t gid_list[NGID]; AZ(seteuid(0)); if (vju_wrkuser != NULL && (jse == JAIL_SUBPROC_VCLLOAD || jse == JAIL_SUBPROC_WORKER)) { AZ(setgid(vju_wrkgid)); AZ(initgroups(vju_wrkuser, vju_wrkgid)); } else { AZ(setgid(vju_gid)); AZ(initgroups(vju_user, vju_gid)); } if (jse == JAIL_SUBPROC_CC && vju_cc_gid_set) { /* Add the optional extra group for the C-compiler access */ i = getgroups(NGID, gid_list); assert(i >= 0); gid_list[i++] = vju_cc_gid; AZ(setgroups(i, gid_list)); } if (vju_wrkuser != NULL && (jse == JAIL_SUBPROC_VCLLOAD || jse == JAIL_SUBPROC_WORKER)) { AZ(setuid(vju_wrkuid)); } else { AZ(setuid(vju_uid)); } #ifdef __linux__ /* * On linux mucking about with uid/gid disables core-dumps, * reenable them again. */ if (prctl(PR_SET_DUMPABLE, 1) != 0) { MGT_complain(C_INFO, "Could not set dumpable bit. Core dumps turned off"); } #endif }
static void mcf_auth(struct cli *cli, const char *const *av, void *priv) { int fd; char buf[CLI_AUTH_RESPONSE_LEN + 1]; AN(av[2]); (void)priv; if (secret_file == NULL) { VCLI_Out(cli, "Secret file not configured\n"); VCLI_SetResult(cli, CLIS_CANT); return; } VJ_master(JAIL_MASTER_FILE); fd = open(secret_file, O_RDONLY); if (fd < 0) { VCLI_Out(cli, "Cannot open secret file (%s)\n", strerror(errno)); VCLI_SetResult(cli, CLIS_CANT); VJ_master(JAIL_MASTER_LOW); return; } VJ_master(JAIL_MASTER_LOW); mgt_got_fd(fd); VCLI_AuthResponse(fd, cli->challenge, buf); AZ(close(fd)); if (strcasecmp(buf, av[2])) { MGT_complain(C_SECURITY, "CLI Authentication failure from %s", cli->ident); VCLI_SetResult(cli, CLIS_CLOSE); return; } cli->auth = MCF_AUTH; memset(cli->challenge, 0, sizeof cli->challenge); VCLI_SetResult(cli, CLIS_OK); mcf_banner(cli, av, priv); }
static void mgt_launch_child(struct cli *cli) { pid_t pid; unsigned u; char *p; struct vev *e; int i, j, k, cp[2]; struct sigaction sa; if (child_state != CH_STOPPED && child_state != CH_DIED) return; if (!MAC_sockets_ready(cli)) { child_state = CH_STOPPED; if (cli != NULL) { VCLI_SetResult(cli, CLIS_CANT); return; } MGT_complain(C_ERR, "Child start failed: could not open sockets"); return; } child_state = CH_STARTING; /* Open pipe for mgr->child CLI */ AZ(pipe(cp)); heritage.cli_in = cp[0]; mgt_child_inherit(heritage.cli_in, "cli_in"); child_cli_out = cp[1]; /* Open pipe for child->mgr CLI */ AZ(pipe(cp)); heritage.cli_out = cp[1]; mgt_child_inherit(heritage.cli_out, "cli_out"); child_cli_in = cp[0]; /* * Open pipe for child stdout/err * NB: not inherited, because we dup2() it to stdout/stderr in child */ AZ(pipe(cp)); heritage.std_fd = cp[1]; child_output = cp[0]; AN(heritage.vsm); mgt_SHM_Size_Adjust(); AN(heritage.vsm); AN(heritage.param); if ((pid = fork()) < 0) { /* XXX */ perror("Could not fork child"); exit(1); } if (pid == 0) { /* Redirect stdin/out/err */ AZ(close(STDIN_FILENO)); assert(open("/dev/null", O_RDONLY) == STDIN_FILENO); assert(dup2(heritage.std_fd, STDOUT_FILENO) == STDOUT_FILENO); assert(dup2(heritage.std_fd, STDERR_FILENO) == STDERR_FILENO); /* * Close all FDs the child shouldn't know about * * We cannot just close these filedescriptors, some random * library routine might miss it later on and wantonly close * a FD we use at that point in time. (See bug #1841). * We close the FD and replace it with /dev/null instead, * That prevents security leakage, and gives the library * code a valid FD to close when it discovers the changed * circumstances. */ closelog(); for (i = STDERR_FILENO + 1; i < CLOSE_FD_UP_TO; i++) { if (vbit_test(fd_map, i)) continue; if (close(i) == 0) { k = open("/dev/null", O_RDONLY); assert(k >= 0); j = dup2(k, i); assert(j == i); AZ(close(k)); } } #ifdef HAVE_SETPROCTITLE setproctitle("Varnish-Chld %s", heritage.name); #endif if (mgt_param.sigsegv_handler) { memset(&sa, 0, sizeof sa); sa.sa_sigaction = child_sigsegv_handler; sa.sa_flags = SA_SIGINFO; (void)sigaction(SIGSEGV, &sa, NULL); (void)sigaction(SIGBUS, &sa, NULL); (void)sigaction(SIGABRT, &sa, NULL); } (void)signal(SIGINT, SIG_DFL); (void)signal(SIGTERM, SIG_DFL); VJ_subproc(JAIL_SUBPROC_WORKER); child_main(); exit(0); } assert(pid > 1); MGT_complain(C_DEBUG, "Child (%jd) Started", (intmax_t)pid); VSC_C_mgt->child_start = ++static_VSC_C_mgt.child_start; /* Close stuff the child got */ closex(&heritage.std_fd); mgt_child_inherit(heritage.cli_in, NULL); closex(&heritage.cli_in); mgt_child_inherit(heritage.cli_out, NULL); closex(&heritage.cli_out); child_std_vlu = VLU_New(NULL, child_line, 0); AN(child_std_vlu); AZ(ev_listen); e = vev_new(); XXXAN(e); e->fd = child_output; e->fd_flags = EV_RD; e->name = "Child listener"; e->callback = child_listener; AZ(vev_add(mgt_evb, e)); ev_listen = e; AZ(ev_poker); if (mgt_param.ping_interval > 0) { e = vev_new(); XXXAN(e); e->timeout = mgt_param.ping_interval; e->callback = child_poker; e->name = "child poker"; AZ(vev_add(mgt_evb, e)); ev_poker = e; } mgt_cli_start_child(child_cli_in, child_cli_out); child_pid = pid; if (mgt_push_vcls_and_start(cli, &u, &p)) { VCLI_SetResult(cli, u); MGT_complain(C_ERR, "Child (%jd) Pushing vcls failed:\n%s", (intmax_t)child_pid, p); free(p); child_state = CH_RUNNING; mgt_stop_child(); } else child_state = CH_RUNNING; }
static void mgt_reap_child(void) { int i; int status = 0xffff; struct vsb *vsb; pid_t r = 0; assert(child_pid != -1); /* * Close the CLI connections * This signals orderly shut down to child */ mgt_cli_stop_child(); if (child_cli_out >= 0) closex(&child_cli_out); if (child_cli_in >= 0) closex(&child_cli_in); /* Stop the poker */ if (ev_poker != NULL) { vev_del(mgt_evb, ev_poker); free(ev_poker); } ev_poker = NULL; /* Stop the listener */ if (ev_listen != NULL) { vev_del(mgt_evb, ev_listen); free(ev_listen); ev_listen = NULL; } /* Compose obituary */ vsb = VSB_new_auto(); XXXAN(vsb); /* Wait for child to die */ for (i = 0; i < mgt_param.cli_timeout; i++) { r = waitpid(child_pid, &status, WNOHANG); if (r == child_pid) break; (void)sleep(1); } if (r == 0) { VSB_printf(vsb, "Child (%jd) not dying, killing", (intmax_t)r); /* Kick it Jim... */ if (MGT_FEATURE(FEATURE_NO_COREDUMP)) (void)kill(child_pid, SIGKILL); else (void)kill(child_pid, SIGQUIT); r = waitpid(child_pid, &status, 0); } if (r != child_pid) fprintf(stderr, "WAIT 0x%jx\n", (uintmax_t)r); assert(r == child_pid); MAC_reopen_sockets(NULL); VSB_printf(vsb, "Child (%jd) %s", (intmax_t)r, status ? "died" : "ended"); if (WIFEXITED(status) && WEXITSTATUS(status)) { VSB_printf(vsb, " status=%d", WEXITSTATUS(status)); exit_status |= 0x20; if (WEXITSTATUS(status) == 1) VSC_C_mgt->child_exit = ++static_VSC_C_mgt.child_exit; else VSC_C_mgt->child_stop = ++static_VSC_C_mgt.child_stop; } if (WIFSIGNALED(status)) { VSB_printf(vsb, " signal=%d", WTERMSIG(status)); exit_status |= 0x40; VSC_C_mgt->child_died = ++static_VSC_C_mgt.child_died; } #ifdef WCOREDUMP if (WCOREDUMP(status)) { VSB_printf(vsb, " (core dumped)"); exit_status |= 0x80; VSC_C_mgt->child_dump = ++static_VSC_C_mgt.child_dump; } #endif AZ(VSB_finish(vsb)); MGT_complain(status ? C_ERR : C_INFO, "%s", VSB_data(vsb)); VSB_delete(vsb); /* Dispose of shared memory but evacuate panic messages first */ if (heritage.panic_str[0] != '\0') { mgt_panic_record(r); mgt_SHM_Destroy(1); VSC_C_mgt->child_panic = ++static_VSC_C_mgt.child_panic; } else { mgt_SHM_Destroy(MGT_DO_DEBUG(DBG_VSM_KEEP)); } mgt_SHM_Create(); mgt_SHM_Commit(); if (child_state == CH_RUNNING) child_state = CH_DIED; /* Pick up any stuff lingering on stdout/stderr */ (void)child_listener(NULL, EV_RD); closex(&child_output); VLU_Destroy(child_std_vlu); child_pid = -1; MGT_complain(C_DEBUG, "Child cleanup complete"); if (child_state == CH_DIED && mgt_param.auto_restart) mgt_launch_child(NULL); else if (child_state == CH_DIED) child_state = CH_STOPPED; else if (child_state == CH_STOPPING) child_state = CH_STOPPED; }
static void mgt_cli_cb_before(const struct cli *cli) { MGT_complain(C_CLI, "CLI %s Rd %s", cli->ident, cli->cmd); }