// SPDX-License-Identifier: CDDL-1.0 /* * 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 * https://opensource.org/license/CDDL-1.0. */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \ ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \ ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL) #define ACL_SYNCHRONIZE_SET_DENY 0x0000001 #define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002 #define ACL_SYNCHRONIZE_ERR_DENY 0x0000004 #define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008 #define ACL_WRITE_OWNER_SET_DENY 0x0000010 #define ACL_WRITE_OWNER_SET_ALLOW 0x0000020 #define ACL_WRITE_OWNER_ERR_DENY 0x0000040 #define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080 #define ACL_DELETE_SET_DENY 0x0000100 #define ACL_DELETE_SET_ALLOW 0x0000200 #define ACL_DELETE_ERR_DENY 0x0000400 #define ACL_DELETE_ERR_ALLOW 0x0000800 #define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000 #define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 #define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000 #define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000 #define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000 #define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000 #define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000 #define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000 #define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000 #define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000 #define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000 #define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000 #define ACL_READ_NAMED_READER_SET_DENY 0x1000000 #define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000 #define ACL_READ_NAMED_READER_ERR_DENY 0x4000000 #define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000 #define ACE_VALID_MASK_BITS (\ ACE_READ_DATA | \ ACE_LIST_DIRECTORY | \ ACE_WRITE_DATA | \ ACE_ADD_FILE | \ ACE_APPEND_DATA | \ ACE_ADD_SUBDIRECTORY | \ ACE_READ_NAMED_ATTRS | \ ACE_WRITE_NAMED_ATTRS | \ ACE_EXECUTE | \ ACE_DELETE_CHILD | \ ACE_READ_ATTRIBUTES | \ ACE_WRITE_ATTRIBUTES | \ ACE_DELETE | \ ACE_READ_ACL | \ ACE_WRITE_ACL | \ ACE_WRITE_OWNER | \ ACE_SYNCHRONIZE) #define ACE_MASK_UNDEFINED 0x80000000 #define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \ ACE_DIRECTORY_INHERIT_ACE | \ ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \ ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \ ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE) /* * ACL conversion helpers */ typedef enum { ace_unused, ace_user_obj, ace_user, ace_group, /* includes GROUP and GROUP_OBJ */ ace_other_obj } ace_to_aent_state_t; typedef struct acevals { uid_t key; avl_node_t avl; uint32_t mask; uint32_t allowed; uint32_t denied; int aent_type; } acevals_t; typedef struct ace_list { acevals_t user_obj; avl_tree_t user; int numusers; acevals_t group_obj; avl_tree_t group; int numgroups; acevals_t other_obj; uint32_t acl_mask; int hasmask; int dfacl_flag; ace_to_aent_state_t state; int seen; /* bitmask of all aclent_t a_type values seen */ } ace_list_t; /* * Generic shellsort, from K&R (1st ed, p 58.), somewhat modified. * v = Ptr to array/vector of objs * n = # objs in the array * s = size of each obj (must be multiples of a word size) * f = ptr to function to compare two objs * returns (-1 = less than, 0 = equal, 1 = greater than */ void ksort(caddr_t v, int n, int s, int (*f)(void *, void *)) { int g, i, j, ii; unsigned int *p1, *p2; unsigned int tmp; /* No work to do */ if (v == NULL || n <= 1) return; /* Sanity check on arguments */ ASSERT3U(((uintptr_t)v & 0x3), ==, 0); ASSERT3S((s & 0x3), ==, 0); ASSERT3S(s, >, 0); for (g = n / 2; g > 0; g /= 2) { for (i = g; i < n; i++) { for (j = i - g; j >= 0 && (*f)(v + j * s, v + (j + g) * s) == 1; j -= g) { p1 = (void *)(v + j * s); p2 = (void *)(v + (j + g) * s); for (ii = 0; ii < s / 4; ii++) { tmp = *p1; *p1++ = *p2; *p2++ = tmp; } } } } } /* * Compare two acls, all fields. Returns: * -1 (less than) * 0 (equal) * +1 (greater than) */ int cmp2acls(void *a, void *b) { aclent_t *x = (aclent_t *)a; aclent_t *y = (aclent_t *)b; /* Compare types */ if (x->a_type < y->a_type) return (-1); if (x->a_type > y->a_type) return (1); /* Equal types; compare id's */ if (x->a_id < y->a_id) return (-1); if (x->a_id > y->a_id) return (1); /* Equal ids; compare perms */ if (x->a_perm < y->a_perm) return (-1); if (x->a_perm > y->a_perm) return (1); /* Totally equal */ return (0); } static int cacl_malloc(void **ptr, size_t size) { *ptr = kmem_zalloc(size, KM_SLEEP); return (0); } #define SET_ACE(acl, index, who, mask, type, flags) { \ acl[0][index].a_who = (uint32_t)who; \ acl[0][index].a_type = type; \ acl[0][index].a_flags = flags; \ acl[0][index++].a_access_mask = mask; \ } void acl_trivial_access_masks(mode_t mode, boolean_t isdir, trivial_acl_t *masks) { uint32_t read_mask = ACE_READ_DATA; uint32_t write_mask = ACE_WRITE_DATA|ACE_APPEND_DATA; uint32_t execute_mask = ACE_EXECUTE; (void) isdir; /* will need this later */ masks->deny1 = 0; if (!(mode & S_IRUSR) && (mode & (S_IRGRP|S_IROTH))) masks->deny1 |= read_mask; if (!(mode & S_IWUSR) && (mode & (S_IWGRP|S_IWOTH))) masks->deny1 |= write_mask; if (!(mode & S_IXUSR) && (mode & (S_IXGRP|S_IXOTH))) masks->deny1 |= execute_mask; masks->deny2 = 0; if (!(mode & S_IRGRP) && (mode & S_IROTH)) masks->deny2 |= read_mask; if (!(mode & S_IWGRP) && (mode & S_IWOTH)) masks->deny2 |= write_mask; if (!(mode & S_IXGRP) && (mode & S_IXOTH)) masks->deny2 |= execute_mask; masks->allow0 = 0; if ((mode & S_IRUSR) && (!(mode & S_IRGRP) && (mode & S_IROTH))) masks->allow0 |= read_mask; if ((mode & S_IWUSR) && (!(mode & S_IWGRP) && (mode & S_IWOTH))) masks->allow0 |= write_mask; if ((mode & S_IXUSR) && (!(mode & S_IXGRP) && (mode & S_IXOTH))) masks->allow0 |= execute_mask; masks->owner = ACE_WRITE_ATTRIBUTES|ACE_WRITE_OWNER|ACE_WRITE_ACL| ACE_WRITE_NAMED_ATTRS|ACE_READ_ACL|ACE_READ_ATTRIBUTES| ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE; if (mode & S_IRUSR) masks->owner |= read_mask; if (mode & S_IWUSR) masks->owner |= write_mask; if (mode & S_IXUSR) masks->owner |= execute_mask; masks->group = ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_NAMED_ATTRS| ACE_SYNCHRONIZE; if (mode & S_IRGRP) masks->group |= read_mask; if (mode & S_IWGRP) masks->group |= write_mask; if (mode & S_IXGRP) masks->group |= execute_mask; masks->everyone = ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_NAMED_ATTRS| ACE_SYNCHRONIZE; if (mode & S_IROTH) masks->everyone |= read_mask; if (mode & S_IWOTH) masks->everyone |= write_mask; if (mode & S_IXOTH) masks->everyone |= execute_mask; } int acl_trivial_create(mode_t mode, boolean_t isdir, ace_t **acl, int *count) { int index = 0; int error; trivial_acl_t masks; *count = 3; acl_trivial_access_masks(mode, isdir, &masks); if (masks.allow0) (*count)++; if (masks.deny1) (*count)++; if (masks.deny2) (*count)++; if ((error = cacl_malloc((void **)acl, *count * sizeof (ace_t))) != 0) return (error); if (masks.allow0) { SET_ACE(acl, index, -1, masks.allow0, ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER); } if (masks.deny1) { SET_ACE(acl, index, -1, masks.deny1, ACE_ACCESS_DENIED_ACE_TYPE, ACE_OWNER); } if (masks.deny2) { SET_ACE(acl, index, -1, masks.deny2, ACE_ACCESS_DENIED_ACE_TYPE, ACE_GROUP|ACE_IDENTIFIER_GROUP); } SET_ACE(acl, index, -1, masks.owner, ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER); SET_ACE(acl, index, -1, masks.group, ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_IDENTIFIER_GROUP|ACE_GROUP); SET_ACE(acl, index, -1, masks.everyone, ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_EVERYONE); return (0); } /* * ace_trivial: * determine whether an ace_t acl is trivial * * Trivialness implies that the acl is composed of only * owner, group, everyone entries. ACL can't * have read_acl denied, and write_owner/write_acl/write_attributes * can only be owner@ entry. */ int ace_trivial_common(void *acep, int aclcnt, uintptr_t (*walk)(void *, uintptr_t, int aclcnt, uint16_t *, uint16_t *, uint32_t *)) { uint16_t flags; uint32_t mask; uint16_t type; uintptr_t cookie = 0; while ((cookie = walk(acep, cookie, aclcnt, &flags, &type, &mask))) { switch (flags & ACE_TYPE_FLAGS) { case ACE_OWNER: case ACE_GROUP|ACE_IDENTIFIER_GROUP: case ACE_EVERYONE: break; default: return (1); } if (flags & (ACE_FILE_INHERIT_ACE| ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE| ACE_INHERIT_ONLY_ACE)) return (1); /* * Special check for some special bits * * Don't allow anybody to deny reading basic * attributes or a files ACL. */ if ((mask & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) && (type == ACE_ACCESS_DENIED_ACE_TYPE)) return (1); /* * Delete permissions are never set by default */ if (mask & (ACE_DELETE|ACE_DELETE_CHILD)) return (1); /* * only allow owner@ to have * write_acl/write_owner/write_attributes/write_xattr/ */ if (type == ACE_ACCESS_ALLOWED_ACE_TYPE && (!(flags & ACE_OWNER) && (mask & (ACE_WRITE_OWNER|ACE_WRITE_ACL| ACE_WRITE_ATTRIBUTES| ACE_WRITE_NAMED_ATTRS)))) return (1); } return (0); }