summaryrefslogtreecommitdiffstats
path: root/sys/kern/sys_capability.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/sys_capability.c')
-rw-r--r--sys/kern/sys_capability.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c
index a19e881..04f98d8 100644
--- a/sys/kern/sys_capability.c
+++ b/sys/kern/sys_capability.c
@@ -116,3 +116,402 @@ cap_getmode(struct thread *td, struct cap_getmode_args *uap)
}
#endif /* CAPABILITY_MODE */
+
+#ifdef CAPABILITIES
+
+/*
+ * struct capability describes a capability, and is hung off of its struct
+ * file f_data field. cap_file and cap_rightss are static once hooked up, as
+ * neither the object it references nor the rights it encapsulates are
+ * permitted to change.
+ */
+struct capability {
+ struct file *cap_object; /* Underlying object's file. */
+ struct file *cap_file; /* Back-pointer to cap's file. */
+ cap_rights_t cap_rights; /* Mask of rights on object. */
+};
+
+/*
+ * Capabilities have a fileops vector, but in practice none should ever be
+ * called except for fo_close, as the capability will normally not be
+ * returned during a file descriptor lookup in the system call code.
+ */
+static fo_rdwr_t capability_read;
+static fo_rdwr_t capability_write;
+static fo_truncate_t capability_truncate;
+static fo_ioctl_t capability_ioctl;
+static fo_poll_t capability_poll;
+static fo_kqfilter_t capability_kqfilter;
+static fo_stat_t capability_stat;
+static fo_close_t capability_close;
+
+static struct fileops capability_ops = {
+ .fo_read = capability_read,
+ .fo_write = capability_write,
+ .fo_truncate = capability_truncate,
+ .fo_ioctl = capability_ioctl,
+ .fo_poll = capability_poll,
+ .fo_kqfilter = capability_kqfilter,
+ .fo_stat = capability_stat,
+ .fo_close = capability_close,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+static struct fileops capability_ops_unpassable = {
+ .fo_read = capability_read,
+ .fo_write = capability_write,
+ .fo_truncate = capability_truncate,
+ .fo_ioctl = capability_ioctl,
+ .fo_poll = capability_poll,
+ .fo_kqfilter = capability_kqfilter,
+ .fo_stat = capability_stat,
+ .fo_close = capability_close,
+ .fo_flags = 0,
+};
+
+static uma_zone_t capability_zone;
+
+static void
+capability_init(void *dummy __unused)
+{
+
+ capability_zone = uma_zcreate("capability", sizeof(struct capability),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ if (capability_zone == NULL)
+ panic("capability_init: capability_zone not initialized");
+}
+SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_ANY, capability_init, NULL);
+
+/*
+ * Test whether a capability grants the requested rights.
+ */
+static int
+cap_check(struct capability *c, cap_rights_t rights)
+{
+
+ if ((c->cap_rights | rights) != c->cap_rights)
+ return (ENOTCAPABLE);
+ return (0);
+}
+
+/*
+ * Extract rights from a capability for monitoring purposes -- not for use in
+ * any other way, as we want to keep all capability permission evaluation in
+ * this one file.
+ */
+cap_rights_t
+cap_rights(struct file *fp_cap)
+{
+ struct capability *c;
+
+ KASSERT(fp_cap->f_type == DTYPE_CAPABILITY,
+ ("cap_rights: !capability"));
+
+ c = fp_cap->f_data;
+ return (c->cap_rights);
+}
+
+/*
+ * System call to create a new capability reference to either an existing
+ * file object or an an existing capability.
+ */
+int
+cap_new(struct thread *td, struct cap_new_args *uap)
+{
+ int error, capfd;
+ int fd = uap->fd;
+ struct file *fp, *fcapp;
+ cap_rights_t rights = uap->rights;
+
+ AUDIT_ARG_FD(fd);
+#ifdef notyet /* capability auditing will follow in a few commits */
+ AUDIT_ARG_RIGHTS(rights);
+#endif
+ error = fget(td, fd, &fp);
+ if (error)
+ return (error);
+ AUDIT_ARG_FILE(td->td_proc, fp);
+ error = kern_capwrap(td, fp, rights, &fcapp, &capfd);
+ if (error)
+ return (error);
+
+ /*
+ * Release our reference to the file (kern_capwrap has held a reference
+ * for the filedesc array).
+ */
+ fdrop(fp, td);
+ td->td_retval[0] = capfd;
+ return (0);
+}
+
+/*
+ * System call to query the rights mask associated with a capability.
+ */
+int
+cap_getrights(struct thread *td, struct cap_getrights_args *uap)
+{
+ struct capability *cp;
+ struct file *fp;
+ int error;
+
+ AUDIT_ARG_FD(uap->fd);
+ error = fgetcap(td, uap->fd, &fp);
+ if (error)
+ return (error);
+ cp = fp->f_data;
+ error = copyout(&cp->cap_rights, uap->rightsp, sizeof(*uap->rightsp));
+ fdrop(fp, td);
+ return (error);
+}
+
+/*
+ * Create a capability to wrap around an existing file.
+ */
+int
+kern_capwrap(struct thread *td, struct file *fp, cap_rights_t rights,
+ struct file **fcappp, int *capfdp)
+{
+ struct capability *cp, *cp_old;
+ struct file *fp_object;
+ int error;
+
+ if ((rights | CAP_MASK_VALID) != CAP_MASK_VALID)
+ return (EINVAL);
+
+ /*
+ * If a new capability is being derived from an existing capability,
+ * then the new capability rights must be a subset of the existing
+ * rights.
+ */
+ if (fp->f_type == DTYPE_CAPABILITY) {
+ cp_old = fp->f_data;
+ if ((cp_old->cap_rights | rights) != cp_old->cap_rights)
+ return (ENOTCAPABLE);
+ }
+
+ /*
+ * Allocate a new file descriptor to hang the capability off of.
+ */
+ error = falloc(td, fcappp, capfdp, fp->f_flag);
+ if (error)
+ return (error);
+
+ /*
+ * Rather than nesting capabilities, directly reference the object an
+ * existing capability references. There's nothing else interesting
+ * to preserve for future use, as we've incorporated the previous
+ * rights mask into the new one. This prevents us from having to
+ * deal with capability chains.
+ */
+ if (fp->f_type == DTYPE_CAPABILITY)
+ fp_object = ((struct capability *)fp->f_data)->cap_object;
+ else
+ fp_object = fp;
+ fhold(fp_object);
+ cp = uma_zalloc(capability_zone, M_WAITOK | M_ZERO);
+ cp->cap_rights = rights;
+ cp->cap_object = fp_object;
+ cp->cap_file = *fcappp;
+ if (fp->f_flag & DFLAG_PASSABLE)
+ finit(*fcappp, fp->f_flag, DTYPE_CAPABILITY, cp,
+ &capability_ops);
+ else
+ finit(*fcappp, fp->f_flag, DTYPE_CAPABILITY, cp,
+ &capability_ops_unpassable);
+
+ /*
+ * Release our private reference (the proc filedesc still has one).
+ */
+ fdrop(*fcappp, td);
+ return (0);
+}
+
+/*
+ * Given a file descriptor, test it against a capability rights mask and then
+ * return the file descriptor on which to actually perform the requested
+ * operation. As long as the reference to fp_cap remains valid, the returned
+ * pointer in *fp will remain valid, so no extra reference management is
+ * required, and the caller should fdrop() fp_cap as normal when done with
+ * both.
+ */
+int
+cap_funwrap(struct file *fp_cap, cap_rights_t rights, struct file **fpp)
+{
+ struct capability *c;
+ int error;
+
+ if (fp_cap->f_type != DTYPE_CAPABILITY) {
+ *fpp = fp_cap;
+ return (0);
+ }
+ c = fp_cap->f_data;
+ error = cap_check(c, rights);
+ if (error)
+ return (error);
+ *fpp = c->cap_object;
+ return (0);
+}
+
+/*
+ * Slightly different routine for memory mapping file descriptors: unwrap the
+ * capability and check CAP_MMAP, but also return a bitmask representing the
+ * maximum mapping rights the capability allows on the object.
+ */
+int
+cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights, u_char *maxprotp,
+ struct file **fpp)
+{
+ struct capability *c;
+ u_char maxprot;
+ int error;
+
+ if (fp_cap->f_type != DTYPE_CAPABILITY) {
+ *fpp = fp_cap;
+ *maxprotp = VM_PROT_ALL;
+ return (0);
+ }
+ c = fp_cap->f_data;
+ error = cap_check(c, rights | CAP_MMAP);
+ if (error)
+ return (error);
+ *fpp = c->cap_object;
+ maxprot = 0;
+ if (c->cap_rights & CAP_READ)
+ maxprot |= VM_PROT_READ;
+ if (c->cap_rights & CAP_WRITE)
+ maxprot |= VM_PROT_WRITE;
+ if (c->cap_rights & CAP_MAPEXEC)
+ maxprot |= VM_PROT_EXECUTE;
+ *maxprotp = maxprot;
+ return (0);
+}
+
+/*
+ * When a capability is closed, simply drop the reference on the underlying
+ * object and free the capability. fdrop() will handle the case where the
+ * underlying object also needs to close, and the caller will have already
+ * performed any object-specific lock or mqueue handling.
+ */
+static int
+capability_close(struct file *fp, struct thread *td)
+{
+ struct capability *c;
+ struct file *fp_object;
+
+ KASSERT(fp->f_type == DTYPE_CAPABILITY,
+ ("capability_close: !capability"));
+
+ c = fp->f_data;
+ fp->f_ops = &badfileops;
+ fp->f_data = NULL;
+ fp_object = c->cap_object;
+ uma_zfree(capability_zone, c);
+ return (fdrop(fp_object, td));
+}
+
+/*
+ * In general, file descriptor operations should never make it to the
+ * capability, only the underlying file descriptor operation vector, so panic
+ * if we do turn up here.
+ */
+static int
+capability_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
+ int flags, struct thread *td)
+{
+
+ panic("capability_read");
+}
+
+static int
+capability_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
+ int flags, struct thread *td)
+{
+
+ panic("capability_write");
+}
+
+static int
+capability_truncate(struct file *fp, off_t length, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ panic("capability_truncate");
+}
+
+static int
+capability_ioctl(struct file *fp, u_long com, void *data,
+ struct ucred *active_cred, struct thread *td)
+{
+
+ panic("capability_ioctl");
+}
+
+static int
+capability_poll(struct file *fp, int events, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ panic("capability_poll");
+}
+
+static int
+capability_kqfilter(struct file *fp, struct knote *kn)
+{
+
+ panic("capability_kqfilter");
+}
+
+static int
+capability_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ panic("capability_stat");
+}
+
+#else /* !CAPABILITIES */
+
+/*
+ * Stub Capability functions for when options CAPABILITIES isn't compiled
+ * into the kernel.
+ */
+int
+cap_new(struct thread *td, struct cap_new_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+cap_getrights(struct thread *td, struct cap_getrights_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+cap_funwrap(struct file *fp_cap, cap_rights_t rights, struct file **fpp)
+{
+
+ KASSERT(fp_cap->f_type != DTYPE_CAPABILITY,
+ ("cap_funwrap: saw capability"));
+
+ *fpp = fp_cap;
+ return (0);
+}
+
+int
+cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights, u_char *maxprotp,
+ struct file **fpp)
+{
+
+ KASSERT(fp_cap->f_type != DTYPE_CAPABILITY,
+ ("cap_funwrap_mmap: saw capability"));
+
+ *fpp = fp_cap;
+ *maxprotp = VM_PROT_ALL;
+ return (0);
+}
+
+#endif /* CAPABILITIES */
+
OpenPOWER on IntegriCloud