/* * Execute an operation on filename relative to zoneid's zone root. If the * file is in the global zone, then the zfcb() callback will simply be called * directly. If the file is in a non-global zone, then zfcb() will be called * both from the global zone's context, and from the non-global zone's context * (from a fork()'ed child that has entered the non-global zone). This is * done to allow the callback to communicate with itself if needed (e.g. to * pass back the file descriptor of an opened file). */ static int dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb, zfoparg_t *zfoparg) { int ctfd; int err; pid_t childpid; siginfo_t info; zfarg_t zfarg; ctid_t ct; if (zoneid != GLOBAL_ZONEID) { /* * We need to access a file that isn't in the global zone. * Accessing non-global zone files from the global zone is * unsafe (due to symlink attacks), we'll need to fork a child * that enters the zone in question and executes the callback * that will operate on the file. * * Before we proceed with this zone tango, we need to create a * new process contract for the child, as required by * zone_enter(). */ errno = 0; ctfd = open64("/system/contract/process/template", O_RDWR); if (ctfd == -1) return (errno); if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 || (err = ct_tmpl_set_informative(ctfd, 0)) != 0 || (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 || (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 || (err = ct_tmpl_activate(ctfd)) != 0) { (void) close(ctfd); return (err); } childpid = fork(); switch (childpid) { case -1: (void) ct_tmpl_clear(ctfd); (void) close(ctfd); return (err); case 0: (void) ct_tmpl_clear(ctfd); (void) close(ctfd); /* * Elevate our privileges as zone_enter() requires all * privileges. */ if ((err = dlmgmt_elevate_privileges()) != 0) _exit(err); if (zone_enter(zoneid) == -1) _exit(errno); if ((err = dlmgmt_drop_privileges()) != 0) _exit(err); break; default: if (contract_latest(&ct) == -1) ct = -1; (void) ct_tmpl_clear(ctfd); (void) close(ctfd); if (waitid(P_PID, childpid, &info, WEXITED) == -1) { (void) contract_abandon_id(ct); return (errno); } (void) contract_abandon_id(ct); if (info.si_status != 0) return (info.si_status); } } zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0); zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID); zfarg.zfarg_filename = filename; zfarg.zfarg_oparg = zfoparg; err = zfcb(&zfarg); if (!zfarg.zfarg_inglobalzone) _exit(err); return (err); }
/* * void method_store_contract() * Store the newly created contract id into local structures and * the repository. If the repository connection is broken it is rebound. */ static void method_store_contract(restarter_inst_t *inst, int type, ctid_t *cid) { int r; boolean_t primary; if (errno = contract_latest(cid)) uu_die("%s: Couldn't get new contract's id", inst->ri_i.i_fmri); primary = !method_is_transient(inst, type); if (!primary) { if (inst->ri_i.i_transient_ctid != 0) { log_framework(LOG_INFO, "%s: transient ctid expected to be 0 but " "was set to %ld\n", inst->ri_i.i_fmri, inst->ri_i.i_transient_ctid); } inst->ri_i.i_transient_ctid = *cid; } else { if (inst->ri_i.i_primary_ctid != 0) { /* * There was an old contract that we transferred. * Remove it. */ method_remove_contract(inst, B_TRUE, B_FALSE); } if (inst->ri_i.i_primary_ctid != 0) { log_framework(LOG_INFO, "%s: primary ctid expected to be 0 but " "was set to %ld\n", inst->ri_i.i_fmri, inst->ri_i.i_primary_ctid); } inst->ri_i.i_primary_ctid = *cid; inst->ri_i.i_primary_ctid_stopped = 0; contract_hash_store(*cid, inst->ri_id); } again: if (inst->ri_mi_deleted) return; r = restarter_store_contract(inst->ri_m_inst, *cid, primary ? RESTARTER_CONTRACT_PRIMARY : RESTARTER_CONTRACT_TRANSIENT); switch (r) { case 0: break; case ECANCELED: inst->ri_mi_deleted = B_TRUE; break; case ECONNABORTED: libscf_handle_rebind(scf_instance_handle(inst->ri_m_inst)); /* FALLTHROUGH */ case EBADF: libscf_reget_instance(inst); goto again; case ENOMEM: case EPERM: case EACCES: case EROFS: uu_die("%s: Couldn't store contract id %ld", inst->ri_i.i_fmri, *cid); /* NOTREACHED */ case EINVAL: default: bad_error("restarter_store_contract", r); } }
static int zsocket(zoneid_t zoneid, const char *path) { char c = 0; ctid_t ct = -1; int _errno = 0; int pid = 0; int sock_fd = 0; int sockfd[2] = {0}; int stat = 0; int tmpl_fd = 0; int flags; struct sockaddr_un addr; size_t addr_len = 0; if (zoneid < 0) { return (-1); } if (path == NULL) { return (-1); } bzero(&addr, sizeof (addr)); pthread_mutex_lock(&lock); if ((tmpl_fd = init_template()) < 0) { pthread_mutex_unlock(&lock); return (-1); } if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) != 0) { (void) ct_tmpl_clear(tmpl_fd); pthread_mutex_unlock(&lock); return (-1); } pid = fork(); debug("fork returned: %d\n", pid); if (pid < 0) { _errno = errno; (void) ct_tmpl_clear(tmpl_fd); close(sockfd[0]); close(sockfd[1]); errno = _errno; pthread_mutex_unlock(&lock); return (-1); } if (pid == 0) { (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) close(sockfd[0]); if (zone_enter(zoneid) != 0) { debug("CHILD: zone_enter(%d) => %s\n", zoneid, strerror(errno)); _exit(1); } debug("CHILD: zone_enter(%d) => %d\n", zoneid, 0); (void) unlink(path); sock_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (sock_fd < 0) { debug("CHILD: socket => %d\n", errno); _exit(2); } fcntl(sock_fd, F_SETFL, O_NONBLOCK); addr.sun_family = AF_UNIX; addr_len = sizeof (addr.sun_family) + snprintf(addr.sun_path, sizeof (addr.sun_path), path); if (bind(sock_fd, (struct sockaddr *) &addr, addr_len) != 0) { debug("CHILD: bind => %d\n", errno); _exit(3); } if (write_fd(sockfd[1], (void *)"", 1, sock_fd) < 0) { debug("CHILD: write_fd => %d\n", errno); _exit(4); } debug("CHILD: write_fd => %d\n", errno); _exit(0); } if (contract_latest(&ct) == -1) { ct = -1; } (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) contract_abandon_id(ct); (void) close(sockfd[1]); debug("PARENT: waitforpid(%d)\n", pid); while ((waitpid(pid, &stat, 0) != pid) && errno != ECHILD) { /* DO NOTHING */; } if (WIFEXITED(stat) == 0) { debug("PARENT: Child didn't exit\n"); _errno = ECHILD; sock_fd = -1; } else { stat = WEXITSTATUS(stat); debug("PARENT: Child exit status %d\n", stat); if (stat == 0) { read_fd(sockfd[0], &c, 1, &sock_fd); } else { _errno = stat; sock_fd = -1; } } close(sockfd[0]); pthread_mutex_unlock(&lock); if (sock_fd < 0) { errno = _errno; } else { if ((flags = fcntl(sock_fd, F_GETFD)) != -1) { flags |= FD_CLOEXEC; (void) fcntl(sock_fd, F_SETFD, flags); } errno = 0; } debug("zsocket returning fd=%d, errno=%d\n", sock_fd, errno); return (sock_fd); }