diff options
| author | Patrick Mooney <pmooney@pfmooney.com> | 2017-04-18 00:30:14 +0000 |
|---|---|---|
| committer | Patrick Mooney <pmooney@pfmooney.com> | 2017-04-20 18:17:38 +0000 |
| commit | 053ab6d63a92aabacf6aec75c7c0b67ee93727dd (patch) | |
| tree | cbb8ffa3341da66a309f1da910d58b8d5ee04834 | |
| parent | ce33465b99b9a4811a15724b9822c0531560d2b5 (diff) | |
| download | illumos-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.c | 210 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_acl.h | 45 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_xattr.c | 199 | ||||
| -rw-r--r-- | usr/src/uts/intel/Makefile.files | 1 |
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 \ |
