Beispiel #1
0
/*
 * 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);
}
Beispiel #2
0
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);
}