156055Srwatson/*- 2180493Srwatson * Copyright (c) 1999-2001, 2008 Robert N. M. Watson 356055Srwatson * All rights reserved. 456055Srwatson * 556055Srwatson * Redistribution and use in source and binary forms, with or without 656055Srwatson * modification, are permitted provided that the following conditions 756055Srwatson * are met: 856055Srwatson * 1. Redistributions of source code must retain the above copyright 956055Srwatson * notice, this list of conditions and the following disclaimer. 1056055Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1156055Srwatson * notice, this list of conditions and the following disclaimer in the 1256055Srwatson * documentation and/or other materials provided with the distribution. 1356055Srwatson * 1456055Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1556055Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1656055Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1756055Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1856055Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1956055Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2056055Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2156055Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2256055Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2356055Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2456055Srwatson * SUCH DAMAGE. 2556055Srwatson */ 2656055Srwatson/* 2756055Srwatson * Support functionality for the POSIX.1e ACL interface 2856055Srwatson * These calls are intended only to be called within the library. 2956055Srwatson */ 3056055Srwatson 3192986Sobrien#include <sys/cdefs.h> 3292986Sobrien__FBSDID("$FreeBSD$"); 3392986Sobrien 3456055Srwatson#include <sys/types.h> 3575185Stmm#include "namespace.h" 3656055Srwatson#include <sys/acl.h> 3775185Stmm#include "un-namespace.h" 3856055Srwatson#include <errno.h> 3956055Srwatson#include <grp.h> 4056055Srwatson#include <pwd.h> 4156055Srwatson#include <stdio.h> 4256055Srwatson#include <stdlib.h> 43194955Strasz#include <string.h> 44194955Strasz#include <assert.h> 4556055Srwatson 4656055Srwatson#include "acl_support.h" 4756055Srwatson 4856055Srwatson#define ACL_STRING_PERM_WRITE 'w' 4956055Srwatson#define ACL_STRING_PERM_READ 'r' 5056055Srwatson#define ACL_STRING_PERM_EXEC 'x' 5156055Srwatson#define ACL_STRING_PERM_NONE '-' 5256055Srwatson 5356055Srwatson/* 54194955Strasz * Return 0, if both ACLs are identical. 55194955Strasz */ 56194955Straszint 57194955Strasz_acl_differs(const acl_t a, const acl_t b) 58194955Strasz{ 59194955Strasz int i; 60194955Strasz struct acl_entry *entrya, *entryb; 61194955Strasz 62194955Strasz assert(_acl_brand(a) == _acl_brand(b)); 63194955Strasz assert(_acl_brand(a) != ACL_BRAND_UNKNOWN); 64194955Strasz assert(_acl_brand(b) != ACL_BRAND_UNKNOWN); 65194955Strasz 66194955Strasz if (a->ats_acl.acl_cnt != b->ats_acl.acl_cnt) 67194955Strasz return (1); 68194955Strasz 69194955Strasz for (i = 0; i < b->ats_acl.acl_cnt; i++) { 70194955Strasz entrya = &(a->ats_acl.acl_entry[i]); 71194955Strasz entryb = &(b->ats_acl.acl_entry[i]); 72194955Strasz 73194955Strasz if (entrya->ae_tag != entryb->ae_tag || 74194955Strasz entrya->ae_id != entryb->ae_id || 75194955Strasz entrya->ae_perm != entryb->ae_perm || 76194955Strasz entrya->ae_entry_type != entryb->ae_entry_type || 77194955Strasz entrya->ae_flags != entryb->ae_flags) 78194955Strasz return (1); 79194955Strasz } 80194955Strasz 81194955Strasz return (0); 82194955Strasz} 83196638Skientzle 84194955Strasz/* 8574191Srwatson * _posix1e_acl_entry_compare -- compare two acl_entry structures to 8674191Srwatson * determine the order they should appear in. Used by _posix1e_acl_sort to 8774191Srwatson * sort ACL entries into the kernel-desired order -- i.e., the order useful 8874191Srwatson * for evaluation and O(n) validity checking. Beter to have an O(nlogn) sort 8974191Srwatson * in userland and an O(n) in kernel than to have both in kernel. 9056055Srwatson */ 9156055Srwatsontypedef int (*compare)(const void *, const void *); 9256055Srwatsonstatic int 9374191Srwatson_posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b) 9456055Srwatson{ 95194955Strasz 96194955Strasz assert(_entry_brand(a) == ACL_BRAND_POSIX); 97194955Strasz assert(_entry_brand(b) == ACL_BRAND_POSIX); 98194955Strasz 9956055Srwatson /* 10056055Srwatson * First, sort between tags -- conveniently defined in the correct 10156055Srwatson * order for verification. 10256055Srwatson */ 10356055Srwatson if (a->ae_tag < b->ae_tag) 10456055Srwatson return (-1); 10556055Srwatson if (a->ae_tag > b->ae_tag) 10656055Srwatson return (1); 10756055Srwatson 10856055Srwatson /* 10956055Srwatson * Next compare uids/gids on appropriate types. 11056055Srwatson */ 11156055Srwatson 11256055Srwatson if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) { 11356055Srwatson if (a->ae_id < b->ae_id) 11456055Srwatson return (-1); 11556055Srwatson if (a->ae_id > b->ae_id) 11656055Srwatson return (1); 11756055Srwatson 11856055Srwatson /* shouldn't be equal, fall through to the invalid case */ 11956055Srwatson } 12056055Srwatson 12156055Srwatson /* 12256055Srwatson * Don't know how to sort multiple entries of the rest--either it's 12356055Srwatson * a bad entry, or there shouldn't be more than one. Ignore and the 12456055Srwatson * validity checker can get it later. 12556055Srwatson */ 12656055Srwatson return (0); 12756055Srwatson} 12856055Srwatson 12956055Srwatson/* 130208785Strasz * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs. 13156055Srwatson */ 132208785Straszvoid 13374191Srwatson_posix1e_acl_sort(acl_t acl) 13456055Srwatson{ 13575928Sjedgar struct acl *acl_int; 13656055Srwatson 13775928Sjedgar acl_int = &acl->ats_acl; 13856055Srwatson 13975928Sjedgar qsort(&acl_int->acl_entry[0], acl_int->acl_cnt, 14075928Sjedgar sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare); 14156055Srwatson} 14256055Srwatson 14356055Srwatson/* 14456625Srwatson * acl_posix1e -- in what situations should we acl_sort before submission? 14556625Srwatson * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or 14656625Srwatson * ACL_TYPE_DEFAULT 14756055Srwatson */ 14856055Srwatsonint 14974191Srwatson_posix1e_acl(acl_t acl, acl_type_t type) 15056055Srwatson{ 15156055Srwatson 152194955Strasz if (_acl_brand(acl) != ACL_BRAND_POSIX) 153194955Strasz return (0); 154194955Strasz 15556625Srwatson return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT)); 15656055Srwatson} 15756055Srwatson 15856055Srwatson/* 15974191Srwatson * _posix1e_acl_check -- given an ACL, check its validity. This is mirrored 16074191Srwatson * from code in sys/kern/kern_acl.c, and if changes are made in one, they 16174191Srwatson * should be made in the other also. This copy of acl_check is made 16274191Srwatson * available * in userland for the benefit of processes wanting to check ACLs 163196638Skientzle * for validity before submitting them to the kernel, or for performing 16456055Srwatson * in userland file system checking. Needless to say, the kernel makes 16556055Srwatson * the real checks on calls to get/setacl. 16656055Srwatson * 16756055Srwatson * See the comments in kernel for explanation -- just briefly, it assumes 16856055Srwatson * an already sorted ACL, and checks based on that assumption. The 16956055Srwatson * POSIX.1e interface, acl_valid(), will perform the sort before calling 17056055Srwatson * this. Returns 0 on success, EINVAL on failure. 17156055Srwatson */ 17256055Srwatsonint 17375928Sjedgar_posix1e_acl_check(acl_t acl) 17456055Srwatson{ 17575928Sjedgar struct acl *acl_int; 17656055Srwatson struct acl_entry *entry; /* current entry */ 177180493Srwatson uid_t highest_uid=0, highest_gid=0; 17856055Srwatson int stage = ACL_USER_OBJ; 17956055Srwatson int i = 0; 18056055Srwatson int count_user_obj=0, count_user=0, count_group_obj=0, 18156055Srwatson count_group=0, count_mask=0, count_other=0; 18256055Srwatson 18375928Sjedgar acl_int = &acl->ats_acl; 18475928Sjedgar 18574191Srwatson /* printf("_posix1e_acl_check: checking acl with %d entries\n", 18674191Srwatson acl->acl_cnt); */ 18775928Sjedgar while (i < acl_int->acl_cnt) { 18875928Sjedgar entry = &acl_int->acl_entry[i]; 18956055Srwatson 19056055Srwatson if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS) 19156055Srwatson return (EINVAL); 19256055Srwatson 19356055Srwatson switch(entry->ae_tag) { 19456055Srwatson case ACL_USER_OBJ: 19574191Srwatson /* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n", 19674191Srwatson i); */ 19756055Srwatson if (stage > ACL_USER_OBJ) 19856055Srwatson return (EINVAL); 19956055Srwatson stage = ACL_USER; 20056055Srwatson count_user_obj++; 20156055Srwatson break; 202196638Skientzle 20356055Srwatson case ACL_USER: 20474191Srwatson /* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */ 20556055Srwatson if (stage > ACL_USER) 20656055Srwatson return (EINVAL); 20756055Srwatson stage = ACL_USER; 20856055Srwatson if (count_user && (entry->ae_id <= highest_uid)) 20956055Srwatson return (EINVAL); 21056055Srwatson highest_uid = entry->ae_id; 21156055Srwatson count_user++; 212196638Skientzle break; 213196638Skientzle 21456055Srwatson case ACL_GROUP_OBJ: 21574191Srwatson /* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n", 21674191Srwatson i); */ 21756055Srwatson if (stage > ACL_GROUP_OBJ) 21856055Srwatson return (EINVAL); 21956055Srwatson stage = ACL_GROUP; 22056055Srwatson count_group_obj++; 22156055Srwatson break; 222196638Skientzle 22356055Srwatson case ACL_GROUP: 22474191Srwatson /* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */ 22556055Srwatson if (stage > ACL_GROUP) 22656055Srwatson return (EINVAL); 22756055Srwatson stage = ACL_GROUP; 22856055Srwatson if (count_group && (entry->ae_id <= highest_gid)) 22956055Srwatson return (EINVAL); 23056055Srwatson highest_gid = entry->ae_id; 23156055Srwatson count_group++; 23256055Srwatson break; 233196638Skientzle 23456055Srwatson case ACL_MASK: 23574191Srwatson /* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */ 23656055Srwatson if (stage > ACL_MASK) 23756055Srwatson return (EINVAL); 23856055Srwatson stage = ACL_MASK; 23956055Srwatson count_mask++; 24056055Srwatson break; 241196638Skientzle 24256055Srwatson case ACL_OTHER: 24374191Srwatson /* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */ 24456055Srwatson if (stage > ACL_OTHER) 24556055Srwatson return (EINVAL); 24656055Srwatson stage = ACL_OTHER; 24756055Srwatson count_other++; 24856055Srwatson break; 249196638Skientzle 25056055Srwatson default: 25174191Srwatson /* printf("_posix1e_acl_check: %d: INVALID\n", i); */ 25256055Srwatson return (EINVAL); 25356055Srwatson } 25456055Srwatson i++; 25556055Srwatson } 25656055Srwatson 25756055Srwatson if (count_user_obj != 1) 25856055Srwatson return (EINVAL); 259196638Skientzle 26056055Srwatson if (count_group_obj != 1) 26156055Srwatson return (EINVAL); 26256055Srwatson 26356055Srwatson if (count_mask != 0 && count_mask != 1) 26456055Srwatson return (EINVAL); 26556055Srwatson 26656055Srwatson if (count_other != 1) 26756055Srwatson return (EINVAL); 26856055Srwatson 26956055Srwatson return (0); 27056055Srwatson} 27156055Srwatson 27256055Srwatson/* 27356055Srwatson * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill 27456055Srwatson * in a string describing the permissions. 27556055Srwatson */ 27656055Srwatsonint 27774191Srwatson_posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf) 27856055Srwatson{ 27956055Srwatson 28074191Srwatson if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) { 28156055Srwatson errno = ENOMEM; 28256055Srwatson return (-1); 28356055Srwatson } 28456055Srwatson 28556055Srwatson if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) { 28656055Srwatson errno = EINVAL; 28756055Srwatson return (-1); 28856055Srwatson } 28956055Srwatson 29056055Srwatson buf[3] = 0; /* null terminate */ 29156055Srwatson 29275404Sjedgar if (perm & ACL_READ) 29356055Srwatson buf[0] = ACL_STRING_PERM_READ; 29456055Srwatson else 29556055Srwatson buf[0] = ACL_STRING_PERM_NONE; 29656055Srwatson 29775404Sjedgar if (perm & ACL_WRITE) 29856055Srwatson buf[1] = ACL_STRING_PERM_WRITE; 29956055Srwatson else 30056055Srwatson buf[1] = ACL_STRING_PERM_NONE; 30156055Srwatson 30275404Sjedgar if (perm & ACL_EXECUTE) 30356055Srwatson buf[2] = ACL_STRING_PERM_EXEC; 30456055Srwatson else 30556055Srwatson buf[2] = ACL_STRING_PERM_NONE; 30656055Srwatson 30756055Srwatson return (0); 30856055Srwatson} 30956055Srwatson 31056055Srwatson/* 31156055Srwatson * given a string, return a permission describing it 31256055Srwatson */ 31356055Srwatsonint 31474191Srwatson_posix1e_acl_string_to_perm(char *string, acl_perm_t *perm) 31556055Srwatson{ 31656055Srwatson acl_perm_t myperm = ACL_PERM_NONE; 31756055Srwatson char *ch; 31856055Srwatson 31956055Srwatson ch = string; 32056055Srwatson while (*ch) { 32156055Srwatson switch(*ch) { 32256055Srwatson case ACL_STRING_PERM_READ: 32375404Sjedgar myperm |= ACL_READ; 32456055Srwatson break; 32556055Srwatson case ACL_STRING_PERM_WRITE: 32675404Sjedgar myperm |= ACL_WRITE; 32756055Srwatson break; 32856055Srwatson case ACL_STRING_PERM_EXEC: 32975404Sjedgar myperm |= ACL_EXECUTE; 33056055Srwatson break; 33156055Srwatson case ACL_STRING_PERM_NONE: 33256055Srwatson break; 33356055Srwatson default: 33456055Srwatson return (EINVAL); 33556055Srwatson } 33656055Srwatson ch++; 33756055Srwatson } 33856055Srwatson 33956055Srwatson *perm = myperm; 34056055Srwatson return (0); 34156055Srwatson} 34256055Srwatson 34356055Srwatson/* 34456055Srwatson * Add an ACL entry without doing much checking, et al 34556055Srwatson */ 34656055Srwatsonint 34774191Srwatson_posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm) 34856055Srwatson{ 34975928Sjedgar struct acl *acl_int; 35056055Srwatson struct acl_entry *e; 35156055Srwatson 35275928Sjedgar acl_int = &acl->ats_acl; 35375928Sjedgar 35475928Sjedgar if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) { 35556055Srwatson errno = ENOMEM; 35656055Srwatson return (-1); 35756055Srwatson } 35856055Srwatson 35975928Sjedgar e = &(acl_int->acl_entry[acl_int->acl_cnt]); 36056055Srwatson e->ae_perm = perm; 36156055Srwatson e->ae_tag = tag; 36256055Srwatson e->ae_id = id; 36375928Sjedgar acl_int->acl_cnt++; 36456055Srwatson 36556055Srwatson return (0); 36656055Srwatson} 367192586Strasz 368192586Strasz/* 369192586Strasz * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" 370194955Strasz * counterpart. It's neccessary for the old (pre-NFSv4 ACLs) binaries 371192586Strasz * to work with new libc and kernel. Fixing 'type' for old binaries with 372192586Strasz * old libc and new kernel is being done by kern/vfs_acl.c:type_unold(). 373192586Strasz */ 374192586Straszint 375192586Strasz_acl_type_unold(acl_type_t type) 376192586Strasz{ 377194955Strasz 378192586Strasz switch (type) { 379192586Strasz case ACL_TYPE_ACCESS_OLD: 380192586Strasz return (ACL_TYPE_ACCESS); 381192586Strasz case ACL_TYPE_DEFAULT_OLD: 382192586Strasz return (ACL_TYPE_DEFAULT); 383192586Strasz default: 384192586Strasz return (type); 385192586Strasz } 386192586Strasz} 387194955Strasz 388194955Straszchar * 389194955Straszstring_skip_whitespace(char *string) 390194955Strasz{ 391194955Strasz 392194955Strasz while (*string && ((*string == ' ') || (*string == '\t'))) 393194955Strasz string++; 394194955Strasz 395194955Strasz return (string); 396194955Strasz} 397194955Strasz 398194955Straszvoid 399194955Straszstring_trim_trailing_whitespace(char *string) 400194955Strasz{ 401194955Strasz char *end; 402194955Strasz 403194955Strasz if (*string == '\0') 404194955Strasz return; 405194955Strasz 406194955Strasz end = string + strlen(string) - 1; 407194955Strasz 408194955Strasz while (end != string) { 409194955Strasz if ((*end == ' ') || (*end == '\t')) { 410194955Strasz *end = '\0'; 411194955Strasz end--; 412194955Strasz } else { 413194955Strasz return; 414194955Strasz } 415194955Strasz } 416194955Strasz 417194955Strasz return; 418194955Strasz} 419