static void tls_call_exec_client(struct proto_conn *sock, const char *srcaddr, const char *dstaddr, int timeout) { char *timeoutstr, *startfdstr, *debugstr; int startfd; /* Declare that we are receiver. */ proto_recv(sock, NULL, 0); if (pjdlog_mode_get() == PJDLOG_MODE_STD) startfd = 3; else /* if (pjdlog_mode_get() == PJDLOG_MODE_SYSLOG) */ startfd = 0; if (proto_descriptor(sock) != startfd) { /* Move socketpair descriptor to descriptor number startfd. */ if (dup2(proto_descriptor(sock), startfd) == -1) pjdlog_exit(EX_OSERR, "dup2() failed"); proto_close(sock); } else { /* * The FD_CLOEXEC is cleared by dup2(2), so when we not * call it, we have to clear it by hand in case it is set. */ if (fcntl(startfd, F_SETFD, 0) == -1) pjdlog_exit(EX_OSERR, "fcntl() failed"); } closefrom(startfd + 1); if (asprintf(&startfdstr, "%d", startfd) == -1) pjdlog_exit(EX_TEMPFAIL, "asprintf() failed"); if (timeout == -1) timeout = TLS_DEFAULT_TIMEOUT; if (asprintf(&timeoutstr, "%d", timeout) == -1) pjdlog_exit(EX_TEMPFAIL, "asprintf() failed"); if (asprintf(&debugstr, "%d", pjdlog_debug_get()) == -1) pjdlog_exit(EX_TEMPFAIL, "asprintf() failed"); execl(proto_get("execpath"), proto_get("execpath"), "proto", "tls", proto_get("user"), "client", startfdstr, srcaddr == NULL ? "" : srcaddr, dstaddr, proto_get("tls:fingerprint"), proto_get("tcp:port"), timeoutstr, debugstr, NULL); pjdlog_exit(EX_SOFTWARE, "execl() failed"); }
void hastd_secondary(struct hast_resource *res, struct nv *nvin) { sigset_t mask; pthread_t td; pid_t pid; int error, mode, debuglevel; /* * Create communication channel between parent and child. */ if (proto_client(NULL, "socketpair://", &res->hr_ctrl) < 0) { KEEP_ERRNO((void)pidfile_remove(pfh)); pjdlog_exit(EX_OSERR, "Unable to create control sockets between parent and child"); } /* * Create communication channel between child and parent. */ if (proto_client(NULL, "socketpair://", &res->hr_event) < 0) { KEEP_ERRNO((void)pidfile_remove(pfh)); pjdlog_exit(EX_OSERR, "Unable to create event sockets between child and parent"); } pid = fork(); if (pid < 0) { KEEP_ERRNO((void)pidfile_remove(pfh)); pjdlog_exit(EX_OSERR, "Unable to fork"); } if (pid > 0) { /* This is parent. */ proto_close(res->hr_remotein); res->hr_remotein = NULL; proto_close(res->hr_remoteout); res->hr_remoteout = NULL; /* Declare that we are receiver. */ proto_recv(res->hr_event, NULL, 0); /* Declare that we are sender. */ proto_send(res->hr_ctrl, NULL, 0); res->hr_workerpid = pid; return; } gres = res; mode = pjdlog_mode_get(); debuglevel = pjdlog_debug_get(); /* Declare that we are sender. */ proto_send(res->hr_event, NULL, 0); /* Declare that we are receiver. */ proto_recv(res->hr_ctrl, NULL, 0); descriptors_cleanup(res); descriptors_assert(res, mode); pjdlog_init(mode); pjdlog_debug_set(debuglevel); pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); setproctitle("%s (%s)", res->hr_name, role2str(res->hr_role)); PJDLOG_VERIFY(sigemptyset(&mask) == 0); PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); /* Error in setting timeout is not critical, but why should it fail? */ if (proto_timeout(res->hr_remotein, 2 * HAST_KEEPALIVE) < 0) pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); if (proto_timeout(res->hr_remoteout, res->hr_timeout) < 0) pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); init_local(res); init_environment(); if (drop_privs(res) != 0) exit(EX_CONFIG); pjdlog_info("Privileges successfully dropped."); /* * Create the control thread before sending any event to the parent, * as we can deadlock when parent sends control request to worker, * but worker has no control thread started yet, so parent waits. * In the meantime worker sends an event to the parent, but parent * is unable to handle the event, because it waits for control * request response. */ error = pthread_create(&td, NULL, ctrl_thread, res); PJDLOG_ASSERT(error == 0); init_remote(res, nvin); event_send(res, EVENT_CONNECT); error = pthread_create(&td, NULL, recv_thread, res); PJDLOG_ASSERT(error == 0); error = pthread_create(&td, NULL, disk_thread, res); PJDLOG_ASSERT(error == 0); (void)send_thread(res); }
static void tls_call_exec_server(struct proto_conn *sock, struct proto_conn *tcp) { int startfd, sockfd, tcpfd, safefd; char *startfdstr, *debugstr; if (pjdlog_mode_get() == PJDLOG_MODE_STD) startfd = 3; else /* if (pjdlog_mode_get() == PJDLOG_MODE_SYSLOG) */ startfd = 0; /* Declare that we are receiver. */ proto_send(sock, NULL, 0); sockfd = proto_descriptor(sock); tcpfd = proto_descriptor(tcp); safefd = MAX(sockfd, tcpfd); safefd = MAX(safefd, startfd); safefd++; /* Move sockfd and tcpfd to safe numbers first. */ if (dup2(sockfd, safefd) == -1) pjdlog_exit(EX_OSERR, "dup2() failed"); proto_close(sock); sockfd = safefd; if (dup2(tcpfd, safefd + 1) == -1) pjdlog_exit(EX_OSERR, "dup2() failed"); proto_close(tcp); tcpfd = safefd + 1; /* Move socketpair descriptor to descriptor number startfd. */ if (dup2(sockfd, startfd) == -1) pjdlog_exit(EX_OSERR, "dup2() failed"); (void)close(sockfd); /* Move tcp descriptor to descriptor number startfd + 1. */ if (dup2(tcpfd, startfd + 1) == -1) pjdlog_exit(EX_OSERR, "dup2() failed"); (void)close(tcpfd); closefrom(startfd + 2); /* * Even if FD_CLOEXEC was set on descriptors before dup2(), it should * have been cleared on dup2(), but better be safe than sorry. */ if (fcntl(startfd, F_SETFD, 0) == -1) pjdlog_exit(EX_OSERR, "fcntl() failed"); if (fcntl(startfd + 1, F_SETFD, 0) == -1) pjdlog_exit(EX_OSERR, "fcntl() failed"); if (asprintf(&startfdstr, "%d", startfd) == -1) pjdlog_exit(EX_TEMPFAIL, "asprintf() failed"); if (asprintf(&debugstr, "%d", pjdlog_debug_get()) == -1) pjdlog_exit(EX_TEMPFAIL, "asprintf() failed"); execl(proto_get("execpath"), proto_get("execpath"), "proto", "tls", proto_get("user"), "server", startfdstr, proto_get("tls:keyfile"), proto_get("tls:certfile"), debugstr, NULL); pjdlog_exit(EX_SOFTWARE, "execl() failed"); }
/* * This function creates sandboxes on-demand whoever has access to it via * 'sock' socket. Function sends two descriptors to the caller: process * descriptor of the sandbox and socket pair descriptor for communication * between sandbox and its owner. */ static void zygote_main(int sock) { int error, fd, flags, procfd; int chanfd[2]; nvlist_t *nvlin, *nvlout; zygote_func_t *func; pid_t pid; assert(sock > STDERR_FILENO); setproctitle("zygote"); if (pjdlog_mode_get() != PJDLOG_MODE_STD) stdnull(); for (fd = STDERR_FILENO + 1; fd < sock; fd++) close(fd); closefrom(sock + 1); for (;;) { nvlin = nvlist_recv(sock); if (nvlin == NULL) { if (errno == ENOTCONN) { /* Casperd exited. */ exit(0); } continue; } func = (zygote_func_t *)(uintptr_t)nvlist_get_number(nvlin, "func"); flags = (int)nvlist_get_number(nvlin, "flags"); nvlist_destroy(nvlin); /* * Someone is requesting a new process, create one. */ procfd = -1; chanfd[0] = -1; chanfd[1] = -1; error = 0; if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, chanfd) == -1) { error = errno; goto send; } pid = pdfork(&procfd, 0); switch (pid) { case -1: /* Failure. */ error = errno; break; case 0: /* Child. */ close(sock); close(chanfd[0]); func(chanfd[1]); /* NOTREACHED */ exit(1); default: /* Parent. */ close(chanfd[1]); break; } send: nvlout = nvlist_create(0); if (error != 0) { nvlist_add_number(nvlout, "error", (uint64_t)error); if (chanfd[0] >= 0) close(chanfd[0]); if (procfd >= 0) close(procfd); } else { nvlist_move_descriptor(nvlout, "chanfd", chanfd[0]); nvlist_move_descriptor(nvlout, "procfd", procfd); } (void)nvlist_send(sock, nvlout); nvlist_destroy(nvlout); } /* NOTREACHED */ }