summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2017-04-18 00:30:14 +0000
committerPatrick Mooney <pmooney@pfmooney.com>2017-04-20 18:17:38 +0000
commit053ab6d63a92aabacf6aec75c7c0b67ee93727dd (patch)
treecbb8ffa3341da66a309f1da910d58b8d5ee04834
parentce33465b99b9a4811a15724b9822c0531560d2b5 (diff)
downloadillumos-joyent-053ab6d63a92aabacf6aec75c7c0b67ee93727dd.tar.gz
OS-5977 lxbrand needs get/setfacl support for FreeIPA
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Ryan Zezeski <ryan.zeseski@joyent.com> Approved by: Ryan Zezeski <ryan.zeseski@joyent.com>
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_acl.c210
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_acl.h45
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_xattr.c199
-rw-r--r--usr/src/uts/intel/Makefile.files1
4 files changed, 425 insertions, 30 deletions
diff --git a/usr/src/uts/common/brand/lx/os/lx_acl.c b/usr/src/uts/common/brand/lx/os/lx_acl.c
new file mode 100644
index 0000000000..594671f401
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_acl.c
@@ -0,0 +1,210 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2017 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/errno.h>
+#include <sys/cred.h>
+#include <sys/sunddi.h>
+#include <sys/pathname.h>
+#include <sys/acl.h>
+#include <acl/acl_common.h>
+#include <sys/lx_acl.h>
+
+
+typedef struct {
+ uint16_t lpaxe_tag;
+ uint16_t lpaxe_perm;
+ uint32_t lpaxe_id;
+} lx_posix_acl_xattr_entry_t;
+
+typedef struct {
+ uint32_t lpaxh_version;
+ lx_posix_acl_xattr_entry_t lpaxh_entries[];
+} lx_posix_acl_xattr_header_t;
+
+#define LX_POSIX_ACL_XATTR_VERSION 0x0002
+
+/* e_tag entry in struct posix_acl_entry */
+#define LX_ACL_USER_OBJ 0x01 /* USER_OBJ */
+#define LX_ACL_USER 0x02 /* USER */
+#define LX_ACL_GROUP_OBJ 0x04 /* GROUP_OBJ */
+#define LX_ACL_GROUP 0x08 /* GROUP */
+#define LX_ACL_MASK 0x10 /* CLASS_OBJ */
+#define LX_ACL_OTHER 0x20 /* OTHER_OBJ */
+
+
+static int
+lx_acl_from_xattr(enum lx_acl_type atype, void *xattr, uint_t xlen,
+ acl_t **aclpp)
+{
+ lx_posix_acl_xattr_header_t *head = xattr;
+ lx_posix_acl_xattr_entry_t *entry;
+ int err = 0;
+ uint_t count, sz = xlen;
+ const uint_t mask = (atype == LX_ACL_DEFAULT) ? ACL_DEFAULT : 0;
+ acl_t *acl;
+ aclent_t *acle;
+
+ if (xattr == NULL) {
+ /* Handle zero-length set operations */
+ acl = acl_alloc(ACLENT_T);
+ *aclpp = acl;
+ return (0);
+ }
+
+ if (xlen < sizeof (*head)) {
+ return (EINVAL);
+ } else if (head->lpaxh_version != LX_POSIX_ACL_XATTR_VERSION) {
+ return (EOPNOTSUPP);
+ }
+
+ sz -= sizeof (lx_posix_acl_xattr_header_t);
+ if (sz % sizeof (lx_posix_acl_xattr_entry_t) != 0) {
+ return (EINVAL);
+ }
+ count = sz / sizeof (lx_posix_acl_xattr_entry_t);
+
+ acl = acl_alloc(ACLENT_T);
+ if (count == 0) {
+ *aclpp = acl;
+ return (0);
+ }
+
+ acle = kmem_alloc(count * sizeof (aclent_t), KM_SLEEP);
+ acl->acl_cnt = count;
+ acl->acl_aclp = acle;
+ entry = head->lpaxh_entries;
+ for (uint_t i = 0; i < count && err == 0; i++, entry++, acle++) {
+ switch (entry->lpaxe_tag) {
+ case LX_ACL_USER_OBJ:
+ case LX_ACL_GROUP_OBJ:
+ case LX_ACL_OTHER:
+ case LX_ACL_MASK:
+ break;
+ case LX_ACL_USER:
+ case LX_ACL_GROUP:
+ if (entry->lpaxe_id > MAXUID) {
+ err = EINVAL;
+ }
+ break;
+ default:
+ err = EINVAL;
+ break;
+ }
+ acle->a_id = entry->lpaxe_id | mask;
+ acle->a_type = entry->lpaxe_tag;
+ acle->a_perm = entry->lpaxe_perm;
+ }
+ if (err != 0) {
+ acl_free(acl);
+ return (err);
+ }
+
+ *aclpp = acl;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+lx_acl_setxattr(vnode_t *vp, enum lx_acl_type atype, void *data, size_t len)
+{
+ const boolean_t is_dir = (vp->v_type == VDIR);
+ acl_t *acl = NULL;
+ cred_t *cr = CRED();
+ int err;
+
+ if (vp->v_type == VLNK) {
+ return (ENOTSUP);
+ } else if (atype == LX_ACL_DEFAULT && !is_dir) {
+ return (EACCES);
+ }
+
+ /*
+ * Copyin and verify the input, even through there is little to be done
+ * with the result.
+ */
+ if ((err = lx_acl_from_xattr(atype, data, len, &acl)) != 0) {
+ return (err);
+ }
+
+ /*
+ * Because systemd has decided to scope-creep its way into a position
+ * of moribund domination over all things system software, there exist
+ * work-arounds which are required to address its numerous bugs and
+ * shortcomings. One such case involves the FreeIPA installer needing
+ * to perform setfacl(3) on /run/systemd/ask-password.
+ *
+ * Between the fact that meaningful ACL translation can be challenging
+ * and that the path in question resides on tmpfs (which doesn't yet
+ * support ACLs at all on illumos), faked success is the only palatable
+ * course of action for now. Atonement will follow.
+ *
+ * See also: https://bugzilla.redhat.com/show_bug.cgi?id=1322167
+ */
+ err = ENOTSUP;
+ if (crgetuid(cr) == 0) {
+ char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ if (vnodetopath(NULL, vp, path, MAXPATHLEN, cr) == 0 &&
+ strncmp(path, "/run/systemd/", 13) == 0) {
+ /* Saccharin-sweet fake success */
+ err = 0;
+ }
+ kmem_free(path, MAXPATHLEN);
+ }
+ acl_free(acl);
+
+ return (err);
+}
+
+/* ARGSUSED */
+int
+lx_acl_getxattr(vnode_t *vp, enum lx_acl_type atype, void *data, size_t slen,
+ ssize_t *solen)
+{
+ const boolean_t is_dir = (vp->v_type == VDIR);
+ vsecattr_t vsattr;
+ int err;
+
+ if (vp->v_type == VLNK) {
+ return (ENOTSUP);
+ } else if (atype == LX_ACL_DEFAULT && !is_dir) {
+ return (ENODATA);
+ }
+
+ bzero(&vsattr, sizeof (vsattr));
+ vsattr.vsa_mask = VSA_ACECNT;
+ if ((err = VOP_GETSECATTR(vp, &vsattr, 0, CRED(), NULL)) != 0) {
+ err = (err == ENOENT) ? ENODATA : err;
+ return (err);
+ }
+
+ return (ENODATA);
+}
+
+/* ARGSUSED */
+int
+lx_acl_removexattr(vnode_t *vp, enum lx_acl_type atype)
+{
+ return (ENODATA);
+}
+
+/* ARGSUSED */
+int
+lx_acl_listxattr(vnode_t *vp, uio_t *uio)
+{
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/sys/lx_acl.h b/usr/src/uts/common/brand/lx/sys/lx_acl.h
new file mode 100644
index 0000000000..1e5ab26407
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_acl.h
@@ -0,0 +1,45 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2017 Joyent, Inc.
+ */
+
+#ifndef _LX_ACL_H
+#define _LX_ACL_H
+
+#include <sys/vnode.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Both fall under the 'system.' namespace */
+#define LX_XATTR_POSIX_ACL_ACCESS "posix_acl_access"
+#define LX_XATTR_POSIX_ACL_DEFAULT "posix_acl_default"
+
+enum lx_acl_type {
+ LX_ACL_ACCESS,
+ LX_ACL_DEFAULT
+};
+
+extern int lx_acl_setxattr(vnode_t *, enum lx_acl_type, void *, size_t);
+extern int lx_acl_getxattr(vnode_t *, enum lx_acl_type, void *, size_t,
+ ssize_t *);
+extern int lx_acl_removexattr(vnode_t *, enum lx_acl_type);
+extern int lx_acl_listxattr(vnode_t *, uio_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_ACL_H */
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_xattr.c b/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
index fcdccdf327..dc6139008f 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2016 Joyent, Inc.
+ * Copyright (c) 2017 Joyent, Inc.
*/
#include <sys/errno.h>
@@ -18,6 +18,7 @@
#include <sys/file.h>
#include <sys/vnode.h>
#include <sys/pathname.h>
+#include <sys/lx_acl.h>
#define LX_XATTR_NAME_MAX 255
@@ -28,7 +29,49 @@
#define LX_XATTR_FLAG_REPLACE 0x2
#define LX_XATTR_FLAGS_VALID (LX_XATTR_FLAG_CREATE | LX_XATTR_FLAG_REPLACE)
-#define LX_CAP_XATTR_NAME "security.capability"
+enum lx_xattr_ns {
+ LX_XATTR_NS_SECURITY,
+ LX_XATTR_NS_SYSTEM,
+ LX_XATTR_NS_TRUSTED,
+ LX_XATTR_NS_USER,
+ LX_XATTR_NS_INVALID /* Catch-all for invalid namespaces */
+};
+
+/* Present under the 'security.' namespace */
+#define LX_XATTR_CAPABILITY "capability"
+
+typedef struct lx_xattr_ns_list {
+ const char *lxnl_name;
+ unsigned lxnl_len;
+ enum lx_xattr_ns lxnl_ns;
+} lx_xattr_ns_list_t;
+
+static lx_xattr_ns_list_t lx_xattr_namespaces[] = {
+ { "user.", 5, LX_XATTR_NS_USER },
+ { "system.", 7, LX_XATTR_NS_SYSTEM },
+ { "trusted.", 8, LX_XATTR_NS_TRUSTED },
+ { "security.", 9, LX_XATTR_NS_SECURITY },
+ { NULL, 0, LX_XATTR_NS_INVALID }
+};
+
+static int
+lx_xattr_parse(const char *name, size_t nlen, const char **key)
+{
+ lx_xattr_ns_list_t *lxn = lx_xattr_namespaces;
+
+ for (; lxn->lxnl_name != NULL; lxn++) {
+ if (nlen < lxn->lxnl_len) {
+ continue;
+ }
+ if (strncmp(lxn->lxnl_name, name, lxn->lxnl_len) == 0) {
+ *key = name + (lxn->lxnl_len);
+ return (lxn->lxnl_ns);
+ }
+ }
+
+ *key = name;
+ return (LX_XATTR_NS_INVALID);
+}
/*
* *xattr() family of functions.
@@ -42,12 +85,13 @@
/* ARGSUSED */
static int
-lx_setxattr_common(vnode_t *vp, char *name, void *value, size_t size,
- int flags)
+lx_setxattr_common(vnode_t *vp, char *name, void *value, size_t sz, int flags)
{
- int error;
+ int error, type;
char name_buf[LX_XATTR_NAME_MAX + 1];
+ const char *key;
size_t name_len;
+ void *buf = NULL;
if ((flags & ~LX_XATTR_FLAGS_VALID) != 0) {
return (EINVAL);
@@ -58,32 +102,60 @@ lx_setxattr_common(vnode_t *vp, char *name, void *value, size_t size,
} else if (error != 0) {
return (EFAULT);
}
- if (size > LX_XATTR_SIZE_MAX) {
- return (E2BIG);
- }
- /*
- * In order to keep package management software happy, despite lacking
- * support for file-based Linux capabilities via xattrs, we fake
- * success when root attempts a setxattr on that attribute.
- */
- if (crgetuid(CRED()) == 0 &&
- strcmp(name_buf, LX_CAP_XATTR_NAME) == 0) {
- return (0);
+ type = lx_xattr_parse(name_buf, name_len, &key);
+
+ if (sz != 0) {
+ if (sz > LX_XATTR_SIZE_MAX) {
+ return (E2BIG);
+ }
+ buf = kmem_alloc(sz, KM_SLEEP);
+ if (copyin(value, buf, sz) != 0) {
+ kmem_free(buf, sz);
+ return (EFAULT);
+ }
}
+ error = EOPNOTSUPP;
+ switch (type) {
+ case LX_XATTR_NS_SECURITY:
+ /*
+ * In order to keep package management software happy, despite
+ * lacking support for file-based Linux capabilities via
+ * xattrs, we fake success when root attempts a setxattr on
+ * that attribute.
+ */
+ if (crgetuid(CRED()) == 0 &&
+ strcmp(key, LX_XATTR_CAPABILITY) == 0) {
+ error = 0;
+ }
+ break;
+ case LX_XATTR_NS_SYSTEM:
+ if (strcmp(key, LX_XATTR_POSIX_ACL_ACCESS) == 0) {
+ error = lx_acl_setxattr(vp, LX_ACL_ACCESS, value, sz);
+ } else if (strcmp(key, LX_XATTR_POSIX_ACL_DEFAULT) == 0) {
+ error = lx_acl_setxattr(vp, LX_ACL_DEFAULT, value, sz);
+ }
+ default:
+ break;
+ }
- return (EOPNOTSUPP);
+ if (buf != NULL) {
+ kmem_free(buf, sz);
+ }
+ return (error);
}
/* ARGSUSED */
static int
-lx_getxattr_common(vnode_t *vp, char *name, char *value, size_t size,
- ssize_t *osize)
+lx_getxattr_common(vnode_t *vp, char *name, char *value, size_t sz,
+ ssize_t *osz)
{
- int error;
+ int error, type;
char name_buf[LX_XATTR_NAME_MAX + 1];
+ const char *key;
size_t name_len;
+ void *buf = NULL;
error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
@@ -91,26 +163,81 @@ lx_getxattr_common(vnode_t *vp, char *name, char *value, size_t size,
} else if (error != 0) {
return (EFAULT);
}
+ if (sz != 0) {
+ if (sz > LX_XATTR_SIZE_MAX) {
+ sz = LX_XATTR_SIZE_MAX;
+ }
+ buf = kmem_alloc(sz, KM_SLEEP);
+ }
- /*
- * Only parameter validation is attempted for now.
- */
- return (EOPNOTSUPP);
+ type = lx_xattr_parse(name_buf, name_len, &key);
+
+ error = EOPNOTSUPP;
+ switch (type) {
+ case LX_XATTR_NS_SYSTEM:
+ if (strcmp(key, LX_XATTR_POSIX_ACL_ACCESS) == 0) {
+ error = lx_acl_getxattr(vp, LX_ACL_ACCESS, buf, sz,
+ osz);
+ } else if (strcmp(key, LX_XATTR_POSIX_ACL_DEFAULT) == 0) {
+ error = lx_acl_getxattr(vp, LX_ACL_DEFAULT, buf, sz,
+ osz);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (error == 0 && buf != NULL) {
+ VERIFY(*osz <= sz);
+
+ if (copyout(buf, value, *osz) != 0) {
+ error = EFAULT;
+ }
+ }
+ if (buf != NULL) {
+ kmem_free(buf, sz);
+ }
+ return (error);
}
/* ARGSUSED */
static int
-lx_listxattr_common(vnode_t *vp, char *list, size_t size, ssize_t *osize)
+lx_listxattr_common(vnode_t *vp, void *value, size_t size, ssize_t *osize)
{
- return (EOPNOTSUPP);
+ struct uio auio;
+ struct iovec aiov;
+ int err = 0;
+
+ aiov.iov_base = value;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = 0;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_resid = size;
+ auio.uio_fmode = 0;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ /*
+ * Call into all the listxattr routines (which may be no-ops) which are
+ * currently implemented.
+ */
+ err = lx_acl_listxattr(vp, &auio);
+
+ if (err == 0) {
+ *osize = size - auio.uio_resid;
+ }
+
+ return (err);
}
/* ARGSUSED */
static int
lx_removexattr_common(vnode_t *vp, char *name)
{
- int error;
+ int error, type;
char name_buf[LX_XATTR_NAME_MAX + 1];
+ const char *key;
size_t name_len;
error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
@@ -120,9 +247,21 @@ lx_removexattr_common(vnode_t *vp, char *name)
return (EFAULT);
}
- /*
- * Only parameter validation is attempted for now.
- */
+
+ type = lx_xattr_parse(name_buf, name_len, &key);
+
+ error = EOPNOTSUPP;
+ switch (type) {
+ case LX_XATTR_NS_SYSTEM:
+ if (strcmp(key, LX_XATTR_POSIX_ACL_ACCESS) == 0) {
+ error = lx_acl_removexattr(vp, LX_ACL_ACCESS);
+ } else if (strcmp(key, LX_XATTR_POSIX_ACL_DEFAULT) == 0) {
+ error = lx_acl_removexattr(vp, LX_ACL_DEFAULT);
+ }
+ default:
+ break;
+ }
+
return (EOPNOTSUPP);
}
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index a1bca02510..65e7dec2f0 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -300,6 +300,7 @@ SN1_BRAND_OBJS = sn1_brand.o sn1_brand_asm.o
S10_BRAND_OBJS = s10_brand.o s10_brand_asm.o
LX_BRAND_OBJS = \
lx_access.o \
+ lx_acl.o \
lx_aio.o \
lx_archdep.o \
lx_auxv.o \