Example #1
0
int
sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
{
	struct filedesc *fdp;
	struct filedescent *fdep;
	u_long *cmdsp, *dstcmds;
	size_t maxcmds, ncmds;
	int16_t count;
	int error, fd;

	fd = uap->fd;
	dstcmds = uap->cmds;
	maxcmds = uap->maxcmds;

	AUDIT_ARG_FD(fd);

	fdp = td->td_proc->p_fd;

	cmdsp = NULL;
	if (dstcmds != NULL) {
		cmdsp = malloc(sizeof(cmdsp[0]) * IOCTLS_MAX_COUNT, M_FILECAPS,
		    M_WAITOK | M_ZERO);
	}

	FILEDESC_SLOCK(fdp);
	fdep = fdeget_locked(fdp, fd);
	if (fdep == NULL) {
		error = EBADF;
		FILEDESC_SUNLOCK(fdp);
		goto out;
	}
	count = fdep->fde_nioctls;
	if (count != -1 && cmdsp != NULL) {
		ncmds = MIN(count, maxcmds);
		memcpy(cmdsp, fdep->fde_ioctls, sizeof(cmdsp[0]) * ncmds);
	}
	FILEDESC_SUNLOCK(fdp);

	/*
	 * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL)
	 * the only sane thing we can do is to not populate the given array and
	 * return CAP_IOCTLS_ALL.
	 */
	if (count != -1) {
		if (cmdsp != NULL) {
			error = copyout(cmdsp, dstcmds,
			    sizeof(cmdsp[0]) * ncmds);
			if (error != 0)
				goto out;
		}
		td->td_retval[0] = count;
	} else {
		td->td_retval[0] = CAP_IOCTLS_ALL;
	}

	error = 0;
out:
	free(cmdsp, M_FILECAPS);
	return (error);
}
/*
 * Test whether a capability grants the given ioctl command.
 * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and
 * ENOTCAPABLE will be returned.
 */
int
cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd)
{
	struct filedescent *fdep;
	u_long *cmds;
	ssize_t ncmds;
	long i;

	KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
		("%s: invalid fd=%d", __func__, fd));

	fdep = fdeget_locked(fdp, fd);
	KASSERT(fdep != NULL,
	    ("%s: invalid fd=%d", __func__, fd));

	ncmds = fdep->fde_nioctls;
	if (ncmds == -1)
		return (0);

	cmds = fdep->fde_ioctls;
	for (i = 0; i < ncmds; i++) {
		if (cmds[i] == cmd)
			return (0);
	}

	return (ENOTCAPABLE);
}
int
kern_cap_rights_limit(struct thread *td, int fd, cap_rights_t *rights)
{
	struct filedesc *fdp;
	struct filedescent *fdep;
	int error;

	fdp = td->td_proc->p_fd;
	FILEDESC_XLOCK(fdp);
	fdep = fdeget_locked(fdp, fd);
	if (fdep == NULL) {
		FILEDESC_XUNLOCK(fdp);
		return (EBADF);
	}
	error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE);
	if (error == 0) {
		fdep->fde_rights = *rights;
		if (!cap_rights_is_set(rights, CAP_IOCTL)) {
			free(fdep->fde_ioctls, M_FILECAPS);
			fdep->fde_ioctls = NULL;
			fdep->fde_nioctls = 0;
		}
		if (!cap_rights_is_set(rights, CAP_FCNTL))
			fdep->fde_fcntls = 0;
	}
	FILEDESC_XUNLOCK(fdp);
	return (error);
}
int
kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds)
{
	struct filedesc *fdp;
	struct filedescent *fdep;
	u_long *ocmds;
	int error;

	AUDIT_ARG_FD(fd);

	if (ncmds > IOCTLS_MAX_COUNT) {
		error = EINVAL;
		goto out_free;
	}

	fdp = td->td_proc->p_fd;
	FILEDESC_XLOCK(fdp);

	fdep = fdeget_locked(fdp, fd);
	if (fdep == NULL) {
		error = EBADF;
		goto out;
	}

	error = cap_ioctl_limit_check(fdep, cmds, ncmds);
	if (error != 0)
		goto out;

	ocmds = fdep->fde_ioctls;
	fdep->fde_ioctls = cmds;
	fdep->fde_nioctls = ncmds;

	cmds = ocmds;
	error = 0;
out:
	FILEDESC_XUNLOCK(fdp);
out_free:
	free(cmds, M_FILECAPS);
	return (error);
}
int
sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
{
	struct filedesc *fdp;
	struct filedescent *fdep;
	uint32_t rights;
	int fd;

	fd = uap->fd;

	AUDIT_ARG_FD(fd);

	fdp = td->td_proc->p_fd;
	FILEDESC_SLOCK(fdp);
	fdep = fdeget_locked(fdp, fd);
	if (fdep == NULL) {
		FILEDESC_SUNLOCK(fdp);
		return (EBADF);
	}
	rights = fdep->fde_fcntls;
	FILEDESC_SUNLOCK(fdp);

	return (copyout(&rights, uap->fcntlrightsp, sizeof(rights)));
}
int
sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
{
	struct filedesc *fdp;
	struct filedescent *fdep;
	uint32_t fcntlrights;
	int fd;

	fd = uap->fd;
	fcntlrights = uap->fcntlrights;

	AUDIT_ARG_FD(fd);
	AUDIT_ARG_FCNTL_RIGHTS(fcntlrights);

	if ((fcntlrights & ~CAP_FCNTL_ALL) != 0)
		return (EINVAL);

	fdp = td->td_proc->p_fd;
	FILEDESC_XLOCK(fdp);

	fdep = fdeget_locked(fdp, fd);
	if (fdep == NULL) {
		FILEDESC_XUNLOCK(fdp);
		return (EBADF);
	}

	if ((fcntlrights & ~fdep->fde_fcntls) != 0) {
		FILEDESC_XUNLOCK(fdp);
		return (ENOTCAPABLE);
	}

	fdep->fde_fcntls = fcntlrights;
	FILEDESC_XUNLOCK(fdp);

	return (0);
}