1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51462Smarks * Common Development and Distribution License (the "License"). 61462Smarks * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221231Smarks * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <stdlib.h> 29789Sahrens #include <string.h> 30789Sahrens #include <unistd.h> 31789Sahrens #include <limits.h> 32789Sahrens #include <grp.h> 33789Sahrens #include <pwd.h> 341462Smarks #include <strings.h> 35789Sahrens #include <sys/types.h> 36789Sahrens #include <sys/acl.h> 37789Sahrens #include <errno.h> 38789Sahrens #include <sys/stat.h> 391420Smarks #include <sys/varargs.h> 40789Sahrens #include <locale.h> 41789Sahrens #include <aclutils.h> 42789Sahrens #include <acl_common.h> 431462Smarks #include <sys/avl.h> 441462Smarks 451462Smarks #define offsetof(s, m) ((size_t)(&(((s *)0)->m))) 46789Sahrens 47789Sahrens #define ACL_PATH 0 48789Sahrens #define ACL_FD 1 49789Sahrens 50789Sahrens #define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \ 51789Sahrens ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \ 52789Sahrens ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL) 53789Sahrens 54789Sahrens 551462Smarks #define ACL_SYNCHRONIZE_SET_DENY 0x0000001 56789Sahrens #define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002 571462Smarks #define ACL_SYNCHRONIZE_ERR_DENY 0x0000004 581462Smarks #define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008 59789Sahrens 601462Smarks #define ACL_WRITE_OWNER_SET_DENY 0x0000010 61789Sahrens #define ACL_WRITE_OWNER_SET_ALLOW 0x0000020 621462Smarks #define ACL_WRITE_OWNER_ERR_DENY 0x0000040 631462Smarks #define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080 64789Sahrens 651462Smarks #define ACL_DELETE_SET_DENY 0x0000100 661462Smarks #define ACL_DELETE_SET_ALLOW 0x0000200 671462Smarks #define ACL_DELETE_ERR_DENY 0x0000400 681462Smarks #define ACL_DELETE_ERR_ALLOW 0x0000800 691462Smarks 701462Smarks #define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000 71789Sahrens #define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 721462Smarks #define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000 731462Smarks #define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000 74789Sahrens 75789Sahrens #define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000 761462Smarks #define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000 771462Smarks #define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000 781462Smarks #define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000 79789Sahrens 801462Smarks #define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000 81789Sahrens #define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000 821462Smarks #define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000 831462Smarks #define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000 841462Smarks 851462Smarks #define ACL_READ_NAMED_READER_SET_DENY 0x1000000 861462Smarks #define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000 871462Smarks #define ACL_READ_NAMED_READER_ERR_DENY 0x4000000 881462Smarks #define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000 891462Smarks 901462Smarks 911462Smarks #define ACE_VALID_MASK_BITS (\ 921462Smarks ACE_READ_DATA | \ 931462Smarks ACE_LIST_DIRECTORY | \ 941462Smarks ACE_WRITE_DATA | \ 951462Smarks ACE_ADD_FILE | \ 961462Smarks ACE_APPEND_DATA | \ 971462Smarks ACE_ADD_SUBDIRECTORY | \ 981462Smarks ACE_READ_NAMED_ATTRS | \ 991462Smarks ACE_WRITE_NAMED_ATTRS | \ 1001462Smarks ACE_EXECUTE | \ 1011462Smarks ACE_DELETE_CHILD | \ 1021462Smarks ACE_READ_ATTRIBUTES | \ 1031462Smarks ACE_WRITE_ATTRIBUTES | \ 1041462Smarks ACE_DELETE | \ 1051462Smarks ACE_READ_ACL | \ 1061462Smarks ACE_WRITE_ACL | \ 1071462Smarks ACE_WRITE_OWNER | \ 1081462Smarks ACE_SYNCHRONIZE) 1091462Smarks 1101462Smarks #define ACE_MASK_UNDEFINED 0x80000000 1111462Smarks 1121462Smarks #define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \ 1131462Smarks ACE_DIRECTORY_INHERIT_ACE | \ 1141462Smarks ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \ 1151462Smarks ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \ 1161462Smarks ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE) 117789Sahrens 1181462Smarks /* 1191462Smarks * ACL conversion helpers 1201462Smarks */ 1211462Smarks 1221462Smarks typedef enum { 1231462Smarks ace_unused, 1241462Smarks ace_user_obj, 1251462Smarks ace_user, 1261462Smarks ace_group, /* includes GROUP and GROUP_OBJ */ 1271462Smarks ace_other_obj 1281462Smarks } ace_to_aent_state_t; 129789Sahrens 1301462Smarks typedef struct acevals { 1311462Smarks uid_t key; 1321462Smarks avl_node_t avl; 1331462Smarks uint32_t mask; 1341462Smarks uint32_t allowed; 1351462Smarks uint32_t denied; 1361462Smarks int aent_type; 1371462Smarks } acevals_t; 1381462Smarks 1391462Smarks typedef struct ace_list { 1401462Smarks acevals_t user_obj; 1411462Smarks avl_tree_t user; 1421462Smarks int numusers; 1431462Smarks acevals_t group_obj; 1441462Smarks avl_tree_t group; 1451462Smarks int numgroups; 1461462Smarks acevals_t other_obj; 1471462Smarks uint32_t acl_mask; 1481462Smarks int hasmask; 1491462Smarks int dfacl_flag; 1501462Smarks ace_to_aent_state_t state; 1511462Smarks int seen; /* bitmask of all aclent_t a_type values seen */ 1521462Smarks } ace_list_t; 1531231Smarks 154789Sahrens typedef union { 155789Sahrens const char *file; 156789Sahrens int fd; 157789Sahrens } acl_inp; 158789Sahrens 159789Sahrens acl_t * 160789Sahrens acl_alloc(enum acl_type type) 161789Sahrens { 162789Sahrens acl_t *aclp; 163789Sahrens 164789Sahrens aclp = malloc(sizeof (acl_t)); 165789Sahrens 166789Sahrens if (aclp == NULL) 167789Sahrens return (NULL); 168789Sahrens 169789Sahrens aclp->acl_aclp = NULL; 170789Sahrens aclp->acl_cnt = 0; 171789Sahrens 172789Sahrens switch (type) { 173789Sahrens case ACE_T: 174789Sahrens aclp->acl_type = ACE_T; 175789Sahrens aclp->acl_entry_size = sizeof (ace_t); 176789Sahrens break; 177789Sahrens case ACLENT_T: 178789Sahrens aclp->acl_type = ACLENT_T; 179789Sahrens aclp->acl_entry_size = sizeof (aclent_t); 180789Sahrens break; 181789Sahrens default: 182789Sahrens acl_free(aclp); 183789Sahrens aclp = NULL; 184789Sahrens } 185789Sahrens return (aclp); 186789Sahrens } 187789Sahrens 188789Sahrens /* 189789Sahrens * Free acl_t structure 190789Sahrens */ 191789Sahrens void 192789Sahrens acl_free(acl_t *aclp) 193789Sahrens { 194789Sahrens if (aclp == NULL) 195789Sahrens return; 196789Sahrens 197789Sahrens if (aclp->acl_aclp) 198789Sahrens free(aclp->acl_aclp); 199789Sahrens free(aclp); 200789Sahrens } 201789Sahrens 202789Sahrens /* 203789Sahrens * Determine whether a file has a trivial ACL 204789Sahrens * returns: 0 = trivial 205789Sahrens * 1 = nontrivial 206789Sahrens * <0 some other system failure, such as ENOENT or EPERM 207789Sahrens */ 208789Sahrens int 209789Sahrens acl_trivial(const char *filename) 210789Sahrens { 211789Sahrens int acl_flavor; 212789Sahrens int aclcnt; 213789Sahrens int cntcmd; 214789Sahrens int val = 0; 215789Sahrens ace_t *acep; 216789Sahrens 217789Sahrens acl_flavor = pathconf(filename, _PC_ACL_ENABLED); 218789Sahrens 219789Sahrens if (acl_flavor == _ACL_ACE_ENABLED) 220789Sahrens cntcmd = ACE_GETACLCNT; 221789Sahrens else 222789Sahrens cntcmd = GETACLCNT; 223789Sahrens 224789Sahrens aclcnt = acl(filename, cntcmd, 0, NULL); 225789Sahrens if (aclcnt > 0) { 226789Sahrens if (acl_flavor == _ACL_ACE_ENABLED) { 2271231Smarks acep = malloc(sizeof (ace_t) * aclcnt); 2281231Smarks if (acep == NULL) 2291231Smarks return (-1); 2301231Smarks if (acl(filename, ACE_GETACL, 2311231Smarks aclcnt, acep) < 0) { 2321231Smarks free(acep); 2331231Smarks return (-1); 2341231Smarks } 235789Sahrens 2361231Smarks val = ace_trivial(acep, aclcnt); 2371231Smarks free(acep); 2381231Smarks 239789Sahrens } else if (aclcnt > MIN_ACL_ENTRIES) 240789Sahrens val = 1; 241789Sahrens } 242789Sahrens return (val); 243789Sahrens } 244789Sahrens 245789Sahrens static uint32_t 246789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow) 247789Sahrens { 248789Sahrens uint32_t access_mask = 0; 249789Sahrens int acl_produce; 250789Sahrens int synchronize_set = 0, write_owner_set = 0; 251789Sahrens int delete_set = 0, write_attrs_set = 0; 252789Sahrens int read_named_set = 0, write_named_set = 0; 253789Sahrens 254789Sahrens acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW | 255789Sahrens ACL_WRITE_ATTRS_OWNER_SET_ALLOW | 256789Sahrens ACL_WRITE_ATTRS_WRITER_SET_DENY); 257789Sahrens 258789Sahrens if (isallow) { 259789Sahrens synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW; 260789Sahrens write_owner_set = ACL_WRITE_OWNER_SET_ALLOW; 261789Sahrens delete_set = ACL_DELETE_SET_ALLOW; 262789Sahrens if (hasreadperm) 263789Sahrens read_named_set = ACL_READ_NAMED_READER_SET_ALLOW; 264789Sahrens if (haswriteperm) 265789Sahrens write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW; 266789Sahrens if (isowner) 267789Sahrens write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; 268789Sahrens else if (haswriteperm) 269789Sahrens write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; 270789Sahrens } else { 271789Sahrens 272789Sahrens synchronize_set = ACL_SYNCHRONIZE_SET_DENY; 273789Sahrens write_owner_set = ACL_WRITE_OWNER_SET_DENY; 274789Sahrens delete_set = ACL_DELETE_SET_DENY; 275789Sahrens if (hasreadperm) 276789Sahrens read_named_set = ACL_READ_NAMED_READER_SET_DENY; 277789Sahrens if (haswriteperm) 278789Sahrens write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY; 279789Sahrens if (isowner) 280789Sahrens write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY; 281789Sahrens else if (haswriteperm) 282789Sahrens write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY; 283789Sahrens else 284789Sahrens /* 285789Sahrens * If the entity is not the owner and does not 286789Sahrens * have write permissions ACE_WRITE_ATTRIBUTES will 287789Sahrens * always go in the DENY ACE. 288789Sahrens */ 289789Sahrens access_mask |= ACE_WRITE_ATTRIBUTES; 290789Sahrens } 291789Sahrens 292789Sahrens if (acl_produce & synchronize_set) 293789Sahrens access_mask |= ACE_SYNCHRONIZE; 294789Sahrens if (acl_produce & write_owner_set) 295789Sahrens access_mask |= ACE_WRITE_OWNER; 296789Sahrens if (acl_produce & delete_set) 297789Sahrens access_mask |= ACE_DELETE; 298789Sahrens if (acl_produce & write_attrs_set) 299789Sahrens access_mask |= ACE_WRITE_ATTRIBUTES; 300789Sahrens if (acl_produce & read_named_set) 301789Sahrens access_mask |= ACE_READ_NAMED_ATTRS; 302789Sahrens if (acl_produce & write_named_set) 303789Sahrens access_mask |= ACE_WRITE_NAMED_ATTRS; 304789Sahrens 305789Sahrens return (access_mask); 306789Sahrens } 307789Sahrens 308789Sahrens /* 309789Sahrens * Given an mode_t, convert it into an access_mask as used 310789Sahrens * by nfsace, assuming aclent_t -> nfsace semantics. 311789Sahrens */ 312789Sahrens static uint32_t 313789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow) 314789Sahrens { 315789Sahrens uint32_t access = 0; 316789Sahrens int haswriteperm = 0; 317789Sahrens int hasreadperm = 0; 318789Sahrens 319789Sahrens if (isallow) { 320789Sahrens haswriteperm = (mode & 02); 321789Sahrens hasreadperm = (mode & 04); 322789Sahrens } else { 323789Sahrens haswriteperm = !(mode & 02); 324789Sahrens hasreadperm = !(mode & 04); 325789Sahrens } 326789Sahrens 327789Sahrens /* 328789Sahrens * The following call takes care of correctly setting the following 329789Sahrens * mask bits in the access_mask: 330789Sahrens * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE, 331789Sahrens * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS 332789Sahrens */ 333789Sahrens access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow); 334789Sahrens 335789Sahrens if (isallow) { 336789Sahrens access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES; 337789Sahrens if (isowner) 338789Sahrens access |= ACE_WRITE_ACL; 339789Sahrens } else { 340789Sahrens if (! isowner) 341789Sahrens access |= ACE_WRITE_ACL; 342789Sahrens } 343789Sahrens 344789Sahrens /* read */ 345789Sahrens if (mode & 04) { 346789Sahrens access |= ACE_READ_DATA; 347789Sahrens } 348789Sahrens /* write */ 349789Sahrens if (mode & 02) { 350789Sahrens access |= ACE_WRITE_DATA | 351789Sahrens ACE_APPEND_DATA; 352789Sahrens if (isdir) 353789Sahrens access |= ACE_DELETE_CHILD; 354789Sahrens } 355789Sahrens /* exec */ 356789Sahrens if (mode & 01) { 357789Sahrens access |= ACE_EXECUTE; 358789Sahrens } 359789Sahrens 360789Sahrens return (access); 361789Sahrens } 362789Sahrens 363789Sahrens /* 364789Sahrens * Given an nfsace (presumably an ALLOW entry), make a 365789Sahrens * corresponding DENY entry at the address given. 366789Sahrens */ 367789Sahrens static void 368789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner) 369789Sahrens { 370789Sahrens (void) memcpy(deny, allow, sizeof (ace_t)); 371789Sahrens 372789Sahrens deny->a_who = allow->a_who; 373789Sahrens 374789Sahrens deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE; 375789Sahrens deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS; 376789Sahrens if (isdir) 377789Sahrens deny->a_access_mask ^= ACE_DELETE_CHILD; 378789Sahrens 379789Sahrens deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER | 380789Sahrens ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | 381789Sahrens ACE_WRITE_NAMED_ATTRS); 382789Sahrens deny->a_access_mask |= access_mask_set((allow->a_access_mask & 383789Sahrens ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner, 384789Sahrens B_FALSE); 385789Sahrens } 386789Sahrens /* 387789Sahrens * Make an initial pass over an array of aclent_t's. Gather 388789Sahrens * information such as an ACL_MASK (if any), number of users, 389789Sahrens * number of groups, and whether the array needs to be sorted. 390789Sahrens */ 391789Sahrens static int 392789Sahrens ln_aent_preprocess(aclent_t *aclent, int n, 393789Sahrens int *hasmask, mode_t *mask, 394789Sahrens int *numuser, int *numgroup, int *needsort) 395789Sahrens { 396789Sahrens int error = 0; 397789Sahrens int i; 398789Sahrens int curtype = 0; 399789Sahrens 400789Sahrens *hasmask = 0; 401789Sahrens *mask = 07; 402789Sahrens *needsort = 0; 403789Sahrens *numuser = 0; 404789Sahrens *numgroup = 0; 405789Sahrens 406789Sahrens for (i = 0; i < n; i++) { 407789Sahrens if (aclent[i].a_type < curtype) 408789Sahrens *needsort = 1; 409789Sahrens else if (aclent[i].a_type > curtype) 410789Sahrens curtype = aclent[i].a_type; 411789Sahrens if (aclent[i].a_type & USER) 412789Sahrens (*numuser)++; 413789Sahrens if (aclent[i].a_type & (GROUP | GROUP_OBJ)) 414789Sahrens (*numgroup)++; 415789Sahrens if (aclent[i].a_type & CLASS_OBJ) { 416789Sahrens if (*hasmask) { 417789Sahrens error = EINVAL; 418789Sahrens goto out; 419789Sahrens } else { 420789Sahrens *hasmask = 1; 421789Sahrens *mask = aclent[i].a_perm; 422789Sahrens } 423789Sahrens } 424789Sahrens } 425789Sahrens 426789Sahrens if ((! *hasmask) && (*numuser + *numgroup > 1)) { 427789Sahrens error = EINVAL; 428789Sahrens goto out; 429789Sahrens } 430789Sahrens 431789Sahrens out: 432789Sahrens return (error); 433789Sahrens } 434789Sahrens 435789Sahrens /* 436789Sahrens * Convert an array of aclent_t into an array of nfsace entries, 437789Sahrens * following POSIX draft -> nfsv4 conversion semantics as outlined in 438789Sahrens * the IETF draft. 439789Sahrens */ 440789Sahrens static int 441789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir) 442789Sahrens { 443789Sahrens int error = 0; 444789Sahrens mode_t mask; 445789Sahrens int numuser, numgroup, needsort; 446789Sahrens int resultsize = 0; 447789Sahrens int i, groupi = 0, skip; 448789Sahrens ace_t *acep, *result = NULL; 449789Sahrens int hasmask; 450789Sahrens 451789Sahrens error = ln_aent_preprocess(aclent, n, &hasmask, &mask, 452789Sahrens &numuser, &numgroup, &needsort); 453789Sahrens if (error != 0) 454789Sahrens goto out; 455789Sahrens 456789Sahrens /* allow + deny for each aclent */ 457789Sahrens resultsize = n * 2; 458789Sahrens if (hasmask) { 459789Sahrens /* 460789Sahrens * stick extra deny on the group_obj and on each 461789Sahrens * user|group for the mask (the group_obj was added 462789Sahrens * into the count for numgroup) 463789Sahrens */ 464789Sahrens resultsize += numuser + numgroup; 465789Sahrens /* ... and don't count the mask itself */ 466789Sahrens resultsize -= 2; 467789Sahrens } 468789Sahrens 469789Sahrens /* sort the source if necessary */ 470789Sahrens if (needsort) 471789Sahrens ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls); 472789Sahrens 473789Sahrens result = acep = calloc(1, resultsize * sizeof (ace_t)); 474789Sahrens if (result == NULL) 475789Sahrens goto out; 476789Sahrens 477789Sahrens for (i = 0; i < n; i++) { 478789Sahrens /* 479789Sahrens * don't process CLASS_OBJ (mask); mask was grabbed in 480789Sahrens * ln_aent_preprocess() 481789Sahrens */ 482789Sahrens if (aclent[i].a_type & CLASS_OBJ) 483789Sahrens continue; 484789Sahrens 485789Sahrens /* If we need an ACL_MASK emulator, prepend it now */ 486789Sahrens if ((hasmask) && 487789Sahrens (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) { 488789Sahrens acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE; 489789Sahrens acep->a_flags = 0; 490789Sahrens if (aclent[i].a_type & GROUP_OBJ) { 491789Sahrens acep->a_who = -1; 492789Sahrens acep->a_flags |= 493789Sahrens (ACE_IDENTIFIER_GROUP|ACE_GROUP); 494789Sahrens } else if (aclent[i].a_type & USER) { 495789Sahrens acep->a_who = aclent[i].a_id; 496789Sahrens } else { 497789Sahrens acep->a_who = aclent[i].a_id; 498789Sahrens acep->a_flags |= ACE_IDENTIFIER_GROUP; 499789Sahrens } 500789Sahrens if (aclent[i].a_type & ACL_DEFAULT) { 501789Sahrens acep->a_flags |= ACE_INHERIT_ONLY_ACE | 502789Sahrens ACE_FILE_INHERIT_ACE | 503789Sahrens ACE_DIRECTORY_INHERIT_ACE; 504789Sahrens } 505789Sahrens /* 506789Sahrens * Set the access mask for the prepended deny 507789Sahrens * ace. To do this, we invert the mask (found 508789Sahrens * in ln_aent_preprocess()) then convert it to an 509789Sahrens * DENY ace access_mask. 510789Sahrens */ 511789Sahrens acep->a_access_mask = mode_to_ace_access((mask ^ 07), 512789Sahrens isdir, 0, 0); 513789Sahrens acep += 1; 514789Sahrens } 515789Sahrens 516789Sahrens /* handle a_perm -> access_mask */ 517789Sahrens acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm, 518789Sahrens isdir, aclent[i].a_type & USER_OBJ, 1); 519789Sahrens 520789Sahrens /* emulate a default aclent */ 521789Sahrens if (aclent[i].a_type & ACL_DEFAULT) { 522789Sahrens acep->a_flags |= ACE_INHERIT_ONLY_ACE | 523789Sahrens ACE_FILE_INHERIT_ACE | 524789Sahrens ACE_DIRECTORY_INHERIT_ACE; 525789Sahrens } 526789Sahrens 527789Sahrens /* 528789Sahrens * handle a_perm and a_id 529789Sahrens * 530789Sahrens * this must be done last, since it involves the 531789Sahrens * corresponding deny aces, which are handled 532789Sahrens * differently for each different a_type. 533789Sahrens */ 534789Sahrens if (aclent[i].a_type & USER_OBJ) { 535789Sahrens acep->a_who = -1; 536789Sahrens acep->a_flags |= ACE_OWNER; 537789Sahrens ace_make_deny(acep, acep + 1, isdir, B_TRUE); 538789Sahrens acep += 2; 539789Sahrens } else if (aclent[i].a_type & USER) { 540789Sahrens acep->a_who = aclent[i].a_id; 541789Sahrens ace_make_deny(acep, acep + 1, isdir, B_FALSE); 542789Sahrens acep += 2; 543789Sahrens } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) { 544789Sahrens if (aclent[i].a_type & GROUP_OBJ) { 545789Sahrens acep->a_who = -1; 546789Sahrens acep->a_flags |= ACE_GROUP; 547789Sahrens } else { 548789Sahrens acep->a_who = aclent[i].a_id; 549789Sahrens } 550789Sahrens acep->a_flags |= ACE_IDENTIFIER_GROUP; 551789Sahrens /* 552789Sahrens * Set the corresponding deny for the group ace. 553789Sahrens * 554789Sahrens * The deny aces go after all of the groups, unlike 555789Sahrens * everything else, where they immediately follow 556789Sahrens * the allow ace. 557789Sahrens * 558789Sahrens * We calculate "skip", the number of slots to 559789Sahrens * skip ahead for the deny ace, here. 560789Sahrens * 561789Sahrens * The pattern is: 562789Sahrens * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3 563789Sahrens * thus, skip is 564789Sahrens * (2 * numgroup) - 1 - groupi 565789Sahrens * (2 * numgroup) to account for MD + A 566789Sahrens * - 1 to account for the fact that we're on the 567789Sahrens * access (A), not the mask (MD) 568789Sahrens * - groupi to account for the fact that we have 569789Sahrens * passed up groupi number of MD's. 570789Sahrens */ 571789Sahrens skip = (2 * numgroup) - 1 - groupi; 572789Sahrens ace_make_deny(acep, acep + skip, isdir, B_FALSE); 573789Sahrens /* 574789Sahrens * If we just did the last group, skip acep past 575789Sahrens * all of the denies; else, just move ahead one. 576789Sahrens */ 577789Sahrens if (++groupi >= numgroup) 578789Sahrens acep += numgroup + 1; 579789Sahrens else 580789Sahrens acep += 1; 581789Sahrens } else if (aclent[i].a_type & OTHER_OBJ) { 582789Sahrens acep->a_who = -1; 583789Sahrens acep->a_flags |= ACE_EVERYONE; 584789Sahrens ace_make_deny(acep, acep + 1, isdir, B_FALSE); 585789Sahrens acep += 2; 586789Sahrens } else { 587789Sahrens error = EINVAL; 588789Sahrens goto out; 589789Sahrens } 590789Sahrens } 591789Sahrens 592789Sahrens *acepp = result; 593789Sahrens *rescount = resultsize; 594789Sahrens 595789Sahrens out: 596789Sahrens if (error != 0) { 597789Sahrens if ((result != NULL) && (resultsize > 0)) { 598789Sahrens free(result); 599789Sahrens } 600789Sahrens } 601789Sahrens 602789Sahrens return (error); 603789Sahrens } 604789Sahrens 605789Sahrens static int 606789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir, 607789Sahrens ace_t **retacep, int *retacecnt) 608789Sahrens { 609789Sahrens ace_t *acep; 610789Sahrens ace_t *dfacep; 611789Sahrens int acecnt = 0; 612789Sahrens int dfacecnt = 0; 613789Sahrens int dfaclstart = 0; 614789Sahrens int dfaclcnt = 0; 615789Sahrens aclent_t *aclp; 616789Sahrens int i; 617789Sahrens int error; 618789Sahrens 619789Sahrens ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls); 620789Sahrens 621789Sahrens for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) { 622789Sahrens if (aclp->a_type & ACL_DEFAULT) 623789Sahrens break; 624789Sahrens } 625789Sahrens 626789Sahrens if (i < aclcnt) { 6271462Smarks dfaclstart = i; 6281462Smarks dfaclcnt = aclcnt - i; 629789Sahrens } 630789Sahrens 631789Sahrens if (dfaclcnt && isdir == 0) { 632789Sahrens return (-1); 633789Sahrens } 634789Sahrens 635789Sahrens error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir); 636789Sahrens if (error) 637789Sahrens return (-1); 638789Sahrens 639789Sahrens if (dfaclcnt) { 640789Sahrens error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt, 641789Sahrens &dfacep, &dfacecnt, isdir); 642789Sahrens if (error) { 643789Sahrens if (acep) { 644789Sahrens free(acep); 645789Sahrens } 646789Sahrens return (-1); 647789Sahrens } 648789Sahrens } 649789Sahrens 6501462Smarks if (dfacecnt != 0) { 6511462Smarks acep = realloc(acep, sizeof (ace_t) * (acecnt + dfacecnt)); 6521462Smarks if (acep == NULL) 6531462Smarks return (-1); 6541462Smarks if (dfaclcnt) { 6551462Smarks (void) memcpy(acep + acecnt, dfacep, 6561462Smarks sizeof (ace_t) * dfacecnt); 6571462Smarks } 658789Sahrens } 659789Sahrens if (dfaclcnt) 660789Sahrens free(dfacep); 661789Sahrens 662789Sahrens *retacecnt = acecnt + dfacecnt; 6631462Smarks *retacep = acep; 6641462Smarks return (0); 6651462Smarks } 6661462Smarks 6671462Smarks static void 6681462Smarks acevals_init(acevals_t *vals, uid_t key) 6691462Smarks { 6701462Smarks bzero(vals, sizeof (*vals)); 6711462Smarks vals->allowed = ACE_MASK_UNDEFINED; 6721462Smarks vals->denied = ACE_MASK_UNDEFINED; 6731462Smarks vals->mask = ACE_MASK_UNDEFINED; 6741462Smarks vals->key = key; 6751462Smarks } 6761462Smarks 6771462Smarks static void 6781462Smarks ace_list_init(ace_list_t *al, int dfacl_flag) 6791462Smarks { 6801462Smarks acevals_init(&al->user_obj, NULL); 6811462Smarks acevals_init(&al->group_obj, NULL); 6821462Smarks acevals_init(&al->other_obj, NULL); 6831462Smarks al->numusers = 0; 6841462Smarks al->numgroups = 0; 6851462Smarks al->acl_mask = 0; 6861462Smarks al->hasmask = 0; 6871462Smarks al->state = ace_unused; 6881462Smarks al->seen = 0; 6891462Smarks al->dfacl_flag = dfacl_flag; 6901462Smarks } 6911462Smarks 6921462Smarks /* 6931462Smarks * Find or create an acevals holder for a given id and avl tree. 6941462Smarks * 6951462Smarks * Note that only one thread will ever touch these avl trees, so 6961462Smarks * there is no need for locking. 6971462Smarks */ 6981462Smarks static acevals_t * 6991462Smarks acevals_find(ace_t *ace, avl_tree_t *avl, int *num) 7001462Smarks { 7011462Smarks acevals_t key, *rc; 7021462Smarks avl_index_t where; 7031462Smarks 7041462Smarks key.key = ace->a_who; 7051462Smarks rc = avl_find(avl, &key, &where); 7061462Smarks if (rc != NULL) 7071462Smarks return (rc); 7081462Smarks 7091462Smarks /* this memory is freed by ln_ace_to_aent()->ace_list_free() */ 7101462Smarks rc = calloc(1, sizeof (acevals_t)); 7111462Smarks if (rc == NULL) 7121462Smarks return (rc); 7131462Smarks acevals_init(rc, ace->a_who); 7141462Smarks avl_insert(avl, rc, where); 7151462Smarks (*num)++; 7161462Smarks 7171462Smarks return (rc); 7181462Smarks } 7191462Smarks 7201462Smarks static int 7211462Smarks access_mask_check(ace_t *acep, int mask_bit, int isowner) 7221462Smarks { 7231462Smarks int set_deny, err_deny; 7241462Smarks int set_allow, err_allow; 7251462Smarks int acl_consume; 7261462Smarks int haswriteperm, hasreadperm; 7271462Smarks 7281462Smarks if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) { 7291462Smarks haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1; 7301462Smarks hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1; 7311462Smarks } else { 7321462Smarks haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0; 7331462Smarks hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0; 7341462Smarks } 7351462Smarks 7361462Smarks acl_consume = (ACL_SYNCHRONIZE_ERR_DENY | 7371462Smarks ACL_DELETE_ERR_DENY | 7381462Smarks ACL_WRITE_OWNER_ERR_DENY | 7391462Smarks ACL_WRITE_OWNER_ERR_ALLOW | 7401462Smarks ACL_WRITE_ATTRS_OWNER_SET_ALLOW | 7411462Smarks ACL_WRITE_ATTRS_OWNER_ERR_DENY | 7421462Smarks ACL_WRITE_ATTRS_WRITER_SET_DENY | 7431462Smarks ACL_WRITE_ATTRS_WRITER_ERR_ALLOW | 7441462Smarks ACL_WRITE_NAMED_WRITER_ERR_DENY | 7451462Smarks ACL_READ_NAMED_READER_ERR_DENY); 7461462Smarks 7471462Smarks if (mask_bit == ACE_SYNCHRONIZE) { 7481462Smarks set_deny = ACL_SYNCHRONIZE_SET_DENY; 7491462Smarks err_deny = ACL_SYNCHRONIZE_ERR_DENY; 7501462Smarks set_allow = ACL_SYNCHRONIZE_SET_ALLOW; 7511462Smarks err_allow = ACL_SYNCHRONIZE_ERR_ALLOW; 7521462Smarks } else if (mask_bit == ACE_WRITE_OWNER) { 7531462Smarks set_deny = ACL_WRITE_OWNER_SET_DENY; 7541462Smarks err_deny = ACL_WRITE_OWNER_ERR_DENY; 7551462Smarks set_allow = ACL_WRITE_OWNER_SET_ALLOW; 7561462Smarks err_allow = ACL_WRITE_OWNER_ERR_ALLOW; 7571462Smarks } else if (mask_bit == ACE_DELETE) { 7581462Smarks set_deny = ACL_DELETE_SET_DENY; 7591462Smarks err_deny = ACL_DELETE_ERR_DENY; 7601462Smarks set_allow = ACL_DELETE_SET_ALLOW; 7611462Smarks err_allow = ACL_DELETE_ERR_ALLOW; 7621462Smarks } else if (mask_bit == ACE_WRITE_ATTRIBUTES) { 7631462Smarks if (isowner) { 7641462Smarks set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY; 7651462Smarks err_deny = ACL_WRITE_ATTRS_OWNER_ERR_DENY; 7661462Smarks set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; 7671462Smarks err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW; 7681462Smarks } else if (haswriteperm) { 7691462Smarks set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY; 7701462Smarks err_deny = ACL_WRITE_ATTRS_WRITER_ERR_DENY; 7711462Smarks set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; 7721462Smarks err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW; 7731462Smarks } else { 7741462Smarks if ((acep->a_access_mask & mask_bit) && 7751462Smarks (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) { 7761462Smarks return (ENOTSUP); 7771462Smarks } 7781462Smarks return (0); 7791462Smarks } 7801462Smarks } else if (mask_bit == ACE_READ_NAMED_ATTRS) { 7811462Smarks if (!hasreadperm) 7821462Smarks return (0); 7831462Smarks 7841462Smarks set_deny = ACL_READ_NAMED_READER_SET_DENY; 7851462Smarks err_deny = ACL_READ_NAMED_READER_ERR_DENY; 7861462Smarks set_allow = ACL_READ_NAMED_READER_SET_ALLOW; 7871462Smarks err_allow = ACL_READ_NAMED_READER_ERR_ALLOW; 7881462Smarks } else if (mask_bit == ACE_WRITE_NAMED_ATTRS) { 7891462Smarks if (!haswriteperm) 7901462Smarks return (0); 7911462Smarks 7921462Smarks set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY; 7931462Smarks err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY; 7941462Smarks set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW; 7951462Smarks err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW; 7961462Smarks } else { 7971462Smarks return (EINVAL); 7981462Smarks } 7991462Smarks 8001462Smarks if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) { 8011462Smarks if (acl_consume & set_deny) { 8021462Smarks if (!(acep->a_access_mask & mask_bit)) { 8031462Smarks return (ENOTSUP); 8041462Smarks } 8051462Smarks } else if (acl_consume & err_deny) { 8061462Smarks if (acep->a_access_mask & mask_bit) { 8071462Smarks return (ENOTSUP); 8081462Smarks } 8091462Smarks } 8101462Smarks } else { 8111462Smarks /* ACE_ACCESS_ALLOWED_ACE_TYPE */ 8121462Smarks if (acl_consume & set_allow) { 8131462Smarks if (!(acep->a_access_mask & mask_bit)) { 8141462Smarks return (ENOTSUP); 8151462Smarks } 8161462Smarks } else if (acl_consume & err_allow) { 8171462Smarks if (acep->a_access_mask & mask_bit) { 8181462Smarks return (ENOTSUP); 8191462Smarks } 8201462Smarks } 8211462Smarks } 822789Sahrens return (0); 823789Sahrens } 824789Sahrens 8251462Smarks static int 8261462Smarks ace_to_aent_legal(ace_t *acep) 8271462Smarks { 8281462Smarks int error = 0; 8291462Smarks int isowner; 8301462Smarks 8311462Smarks /* only ALLOW or DENY */ 8321462Smarks if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) && 8331462Smarks (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) { 8341462Smarks error = ENOTSUP; 8351462Smarks goto out; 8361462Smarks } 8371462Smarks 8381462Smarks /* check for invalid flags */ 8391462Smarks if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) { 8401462Smarks error = EINVAL; 8411462Smarks goto out; 8421462Smarks } 8431462Smarks 8441462Smarks /* some flags are illegal */ 8451462Smarks if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG | 8461462Smarks ACE_FAILED_ACCESS_ACE_FLAG | 8471462Smarks ACE_NO_PROPAGATE_INHERIT_ACE)) { 8481462Smarks error = ENOTSUP; 8491462Smarks goto out; 8501462Smarks } 8511462Smarks 8521462Smarks /* check for invalid masks */ 8531462Smarks if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) { 8541462Smarks error = EINVAL; 8551462Smarks goto out; 8561462Smarks } 8571462Smarks 8581462Smarks if ((acep->a_flags & ACE_OWNER)) { 8591462Smarks isowner = 1; 8601462Smarks } else { 8611462Smarks isowner = 0; 8621462Smarks } 8631462Smarks 8641462Smarks error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner); 8651462Smarks if (error) 8661462Smarks goto out; 8671462Smarks 8681462Smarks error = access_mask_check(acep, ACE_WRITE_OWNER, isowner); 8691462Smarks if (error) 8701462Smarks goto out; 8711462Smarks 8721462Smarks error = access_mask_check(acep, ACE_DELETE, isowner); 8731462Smarks if (error) 8741462Smarks goto out; 8751462Smarks 8761462Smarks error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner); 8771462Smarks if (error) 8781462Smarks goto out; 8791462Smarks 8801462Smarks error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner); 8811462Smarks if (error) 8821462Smarks goto out; 8831462Smarks 8841462Smarks error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner); 8851462Smarks if (error) 8861462Smarks goto out; 8871462Smarks 8881462Smarks /* more detailed checking of masks */ 8891462Smarks if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { 8901462Smarks if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) { 8911462Smarks error = ENOTSUP; 8921462Smarks goto out; 8931462Smarks } 8941462Smarks if ((acep->a_access_mask & ACE_WRITE_DATA) && 8951462Smarks (! (acep->a_access_mask & ACE_APPEND_DATA))) { 8961462Smarks error = ENOTSUP; 8971462Smarks goto out; 8981462Smarks } 8991462Smarks if ((! (acep->a_access_mask & ACE_WRITE_DATA)) && 9001462Smarks (acep->a_access_mask & ACE_APPEND_DATA)) { 9011462Smarks error = ENOTSUP; 9021462Smarks goto out; 9031462Smarks } 9041462Smarks } 9051462Smarks 9061462Smarks /* ACL enforcement */ 9071462Smarks if ((acep->a_access_mask & ACE_READ_ACL) && 9081462Smarks (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) { 9091462Smarks error = ENOTSUP; 9101462Smarks goto out; 9111462Smarks } 9121462Smarks if (acep->a_access_mask & ACE_WRITE_ACL) { 9131462Smarks if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) && 9141462Smarks (isowner)) { 9151462Smarks error = ENOTSUP; 9161462Smarks goto out; 9171462Smarks } 9181462Smarks if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) && 9191462Smarks (! isowner)) { 9201462Smarks error = ENOTSUP; 9211462Smarks goto out; 9221462Smarks } 9231462Smarks } 9241462Smarks 9251462Smarks out: 9261462Smarks return (error); 9271462Smarks } 9281462Smarks 9291462Smarks static int 9301462Smarks ace_mask_to_mode(uint32_t mask, o_mode_t *modep, int isdir) 9311462Smarks { 9321462Smarks int error = 0; 9331462Smarks o_mode_t mode = 0; 9341462Smarks uint32_t bits, wantbits; 9351462Smarks 9361462Smarks /* read */ 9371462Smarks if (mask & ACE_READ_DATA) 9381462Smarks mode |= 04; 9391462Smarks 9401462Smarks /* write */ 9411462Smarks wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA); 9421462Smarks if (isdir) 9431462Smarks wantbits |= ACE_DELETE_CHILD; 9441462Smarks bits = mask & wantbits; 9451462Smarks if (bits != 0) { 9461462Smarks if (bits != wantbits) { 9471462Smarks error = ENOTSUP; 9481462Smarks goto out; 9491462Smarks } 9501462Smarks mode |= 02; 9511462Smarks } 9521462Smarks 9531462Smarks /* exec */ 9541462Smarks if (mask & ACE_EXECUTE) { 9551462Smarks mode |= 01; 9561462Smarks } 9571462Smarks 9581462Smarks *modep = mode; 9591462Smarks 9601462Smarks out: 9611462Smarks return (error); 9621462Smarks } 9631462Smarks 9641462Smarks static int 9651462Smarks ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir) 9661462Smarks { 9671462Smarks /* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */ 9681462Smarks if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) != 9691462Smarks (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) { 9701462Smarks return (ENOTSUP); 9711462Smarks } 9721462Smarks 9731462Smarks return (ace_mask_to_mode(mask, modep, isdir)); 9741462Smarks } 9751462Smarks 9761462Smarks static int 9771462Smarks acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list, 9781462Smarks uid_t owner, gid_t group, int isdir) 9791462Smarks { 9801462Smarks int error; 9811462Smarks uint32_t flips = ACE_POSIX_SUPPORTED_BITS; 9821462Smarks 9831462Smarks if (isdir) 9841462Smarks flips |= ACE_DELETE_CHILD; 9851462Smarks if (vals->allowed != (vals->denied ^ flips)) { 9861462Smarks error = ENOTSUP; 9871462Smarks goto out; 9881462Smarks } 9891462Smarks if ((list->hasmask) && (list->acl_mask != vals->mask) && 9901462Smarks (vals->aent_type & (USER | GROUP | GROUP_OBJ))) { 9911462Smarks error = ENOTSUP; 9921462Smarks goto out; 9931462Smarks } 9941462Smarks error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir); 9951462Smarks if (error != 0) 9961462Smarks goto out; 9971462Smarks dest->a_type = vals->aent_type; 9981462Smarks if (dest->a_type & (USER | GROUP)) { 9991462Smarks dest->a_id = vals->key; 10001462Smarks } else if (dest->a_type & USER_OBJ) { 10011462Smarks dest->a_id = owner; 10021462Smarks } else if (dest->a_type & GROUP_OBJ) { 10031462Smarks dest->a_id = group; 10041462Smarks } else if (dest->a_type & OTHER_OBJ) { 10051462Smarks dest->a_id = 0; 10061462Smarks } else { 10071462Smarks error = EINVAL; 10081462Smarks goto out; 10091462Smarks } 10101462Smarks 10111462Smarks out: 10121462Smarks return (error); 10131462Smarks } 1014789Sahrens 1015789Sahrens static int 10161462Smarks ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt, 10171462Smarks uid_t owner, gid_t group, int isdir) 10181462Smarks { 10191462Smarks int error = 0; 10201462Smarks aclent_t *aent, *result = NULL; 10211462Smarks acevals_t *vals; 10221462Smarks int resultcount; 10231462Smarks 10241462Smarks if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) != 10251462Smarks (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) { 10261462Smarks error = ENOTSUP; 10271462Smarks goto out; 10281462Smarks } 10291462Smarks if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) { 10301462Smarks error = ENOTSUP; 10311462Smarks goto out; 10321462Smarks } 10331462Smarks 10341462Smarks resultcount = 3 + list->numusers + list->numgroups; 10351462Smarks /* 10361462Smarks * This must be the same condition as below, when we add the CLASS_OBJ 10371462Smarks * (aka ACL mask) 10381462Smarks */ 10391462Smarks if ((list->hasmask) || (! list->dfacl_flag)) 10401462Smarks resultcount += 1; 10411462Smarks 10421462Smarks result = aent = calloc(1, resultcount * sizeof (aclent_t)); 10431462Smarks 10441462Smarks if (result == NULL) { 10451462Smarks error = ENOMEM; 10461462Smarks goto out; 10471462Smarks } 10481462Smarks 10491462Smarks /* USER_OBJ */ 10501462Smarks if (!(list->user_obj.aent_type & USER_OBJ)) { 10511462Smarks error = EINVAL; 10521462Smarks goto out; 10531462Smarks } 10541462Smarks 10551462Smarks error = acevals_to_aent(&list->user_obj, aent, list, owner, group, 10561462Smarks isdir); 10571462Smarks 10581462Smarks if (error != 0) 10591462Smarks goto out; 10601462Smarks ++aent; 10611462Smarks /* USER */ 10621462Smarks vals = NULL; 10631462Smarks for (vals = avl_first(&list->user); vals != NULL; 10641462Smarks vals = AVL_NEXT(&list->user, vals)) { 10651462Smarks if (!(vals->aent_type & USER)) { 10661462Smarks error = EINVAL; 10671462Smarks goto out; 10681462Smarks } 10691462Smarks error = acevals_to_aent(vals, aent, list, owner, group, 10701462Smarks isdir); 10711462Smarks if (error != 0) 10721462Smarks goto out; 10731462Smarks ++aent; 10741462Smarks } 10751462Smarks /* GROUP_OBJ */ 10761462Smarks if (!(list->group_obj.aent_type & GROUP_OBJ)) { 10771462Smarks error = EINVAL; 10781462Smarks goto out; 10791462Smarks } 10801462Smarks error = acevals_to_aent(&list->group_obj, aent, list, owner, group, 10811462Smarks isdir); 10821462Smarks if (error != 0) 10831462Smarks goto out; 10841462Smarks ++aent; 10851462Smarks /* GROUP */ 10861462Smarks vals = NULL; 10871462Smarks for (vals = avl_first(&list->group); vals != NULL; 10881462Smarks vals = AVL_NEXT(&list->group, vals)) { 10891462Smarks if (!(vals->aent_type & GROUP)) { 10901462Smarks error = EINVAL; 10911462Smarks goto out; 10921462Smarks } 10931462Smarks error = acevals_to_aent(vals, aent, list, owner, group, 10941462Smarks isdir); 10951462Smarks if (error != 0) 10961462Smarks goto out; 10971462Smarks ++aent; 10981462Smarks } 10991462Smarks /* 11001462Smarks * CLASS_OBJ (aka ACL_MASK) 11011462Smarks * 11021462Smarks * An ACL_MASK is not fabricated if the ACL is a default ACL. 11031462Smarks * This is to follow UFS's behavior. 11041462Smarks */ 11051462Smarks if ((list->hasmask) || (! list->dfacl_flag)) { 11061462Smarks if (list->hasmask) { 11071462Smarks uint32_t flips = ACE_POSIX_SUPPORTED_BITS; 11081462Smarks if (isdir) 11091462Smarks flips |= ACE_DELETE_CHILD; 11101462Smarks error = ace_mask_to_mode(list->acl_mask ^ flips, 11111462Smarks &aent->a_perm, isdir); 11121462Smarks if (error != 0) 11131462Smarks goto out; 11141462Smarks } else { 11151462Smarks /* fabricate the ACL_MASK from the group permissions */ 11161462Smarks error = ace_mask_to_mode(list->group_obj.allowed, 11171462Smarks &aent->a_perm, isdir); 11181462Smarks if (error != 0) 11191462Smarks goto out; 11201462Smarks } 11211462Smarks aent->a_id = 0; 11221462Smarks aent->a_type = CLASS_OBJ | list->dfacl_flag; 11231462Smarks ++aent; 11241462Smarks } 11251462Smarks /* OTHER_OBJ */ 11261462Smarks if (!(list->other_obj.aent_type & OTHER_OBJ)) { 11271462Smarks error = EINVAL; 11281462Smarks goto out; 11291462Smarks } 11301462Smarks error = acevals_to_aent(&list->other_obj, aent, list, owner, group, 11311462Smarks isdir); 11321462Smarks if (error != 0) 11331462Smarks goto out; 11341462Smarks ++aent; 11351462Smarks 11361462Smarks *aclentp = result; 11371462Smarks *aclcnt = resultcount; 11381462Smarks 11391462Smarks out: 11401462Smarks if (error != 0) { 11411462Smarks if (result != NULL) 11421462Smarks free(result); 11431462Smarks } 11441462Smarks 11451462Smarks return (error); 11461462Smarks } 11471462Smarks 11481462Smarks /* 11491462Smarks * free all data associated with an ace_list 11501462Smarks */ 11511462Smarks static void 11521462Smarks ace_list_free(ace_list_t *al) 11531462Smarks { 11541462Smarks acevals_t *node; 11551462Smarks void *cookie; 11561462Smarks 11571462Smarks if (al == NULL) 11581462Smarks return; 11591462Smarks 11601462Smarks cookie = NULL; 11611462Smarks while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL) 11621462Smarks free(node); 11631462Smarks cookie = NULL; 11641462Smarks while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL) 11651462Smarks free(node); 11661462Smarks 11671462Smarks avl_destroy(&al->user); 11681462Smarks avl_destroy(&al->group); 11691462Smarks 11701462Smarks /* free the container itself */ 11711462Smarks free(al); 11721462Smarks } 11731462Smarks 11741462Smarks static int 11751462Smarks acevals_compare(const void *va, const void *vb) 11761462Smarks { 11771462Smarks const acevals_t *a = va, *b = vb; 11781462Smarks 11791462Smarks if (a->key == b->key) 11801462Smarks return (0); 11811462Smarks 11821462Smarks if (a->key > b->key) 11831462Smarks return (1); 11841462Smarks 11851462Smarks else 11861462Smarks return (-1); 11871462Smarks } 11881462Smarks 11891462Smarks /* 11901462Smarks * Convert a list of ace_t entries to equivalent regular and default 11911462Smarks * aclent_t lists. Return error (ENOTSUP) when conversion is not possible. 11921462Smarks */ 11931462Smarks static int 11941462Smarks ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group, 11951462Smarks aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt, 11961462Smarks int isdir) 11971462Smarks { 11981462Smarks int error = 0; 11991462Smarks ace_t *acep; 12001462Smarks uint32_t bits; 12011462Smarks int i; 12021462Smarks ace_list_t *normacl = NULL, *dfacl = NULL, *acl; 12031462Smarks acevals_t *vals; 12041462Smarks 12051462Smarks *aclentp = NULL; 12061462Smarks *aclcnt = 0; 12071462Smarks *dfaclentp = NULL; 12081462Smarks *dfaclcnt = 0; 12091462Smarks 12101462Smarks /* we need at least user_obj, group_obj, and other_obj */ 12111462Smarks if (n < 6) { 12121462Smarks error = ENOTSUP; 12131462Smarks goto out; 12141462Smarks } 12151462Smarks if (ace == NULL) { 12161462Smarks error = EINVAL; 12171462Smarks goto out; 12181462Smarks } 12191462Smarks 12201462Smarks normacl = calloc(1, sizeof (ace_list_t)); 12211462Smarks 12221462Smarks if (normacl == NULL) { 12231462Smarks error = errno; 12241462Smarks goto out; 12251462Smarks } 12261462Smarks 12271462Smarks avl_create(&normacl->user, acevals_compare, sizeof (acevals_t), 12281462Smarks offsetof(acevals_t, avl)); 12291462Smarks avl_create(&normacl->group, acevals_compare, sizeof (acevals_t), 12301462Smarks offsetof(acevals_t, avl)); 12311462Smarks 12321462Smarks ace_list_init(normacl, 0); 12331462Smarks 12341462Smarks dfacl = calloc(1, sizeof (ace_list_t)); 12351462Smarks if (dfacl == NULL) { 12361462Smarks error = errno; 12371462Smarks goto out; 12381462Smarks } 12391462Smarks avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t), 12401462Smarks offsetof(acevals_t, avl)); 12411462Smarks avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t), 12421462Smarks offsetof(acevals_t, avl)); 12431462Smarks ace_list_init(dfacl, ACL_DEFAULT); 12441462Smarks 12451462Smarks /* process every ace_t... */ 12461462Smarks for (i = 0; i < n; i++) { 12471462Smarks acep = &ace[i]; 12481462Smarks 12491462Smarks /* rule out certain cases quickly */ 12501462Smarks error = ace_to_aent_legal(acep); 12511462Smarks if (error != 0) 12521462Smarks goto out; 12531462Smarks 12541462Smarks /* 12551462Smarks * Turn off these bits in order to not have to worry about 12561462Smarks * them when doing the checks for compliments. 12571462Smarks */ 12581462Smarks acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE | 12591462Smarks ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES | 12601462Smarks ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS); 12611462Smarks 12621462Smarks /* see if this should be a regular or default acl */ 12631462Smarks bits = acep->a_flags & 12641462Smarks (ACE_INHERIT_ONLY_ACE | 12651462Smarks ACE_FILE_INHERIT_ACE | 12661462Smarks ACE_DIRECTORY_INHERIT_ACE); 12671462Smarks if (bits != 0) { 12681462Smarks /* all or nothing on these inherit bits */ 12691462Smarks if (bits != (ACE_INHERIT_ONLY_ACE | 12701462Smarks ACE_FILE_INHERIT_ACE | 12711462Smarks ACE_DIRECTORY_INHERIT_ACE)) { 12721462Smarks error = ENOTSUP; 12731462Smarks goto out; 12741462Smarks } 12751462Smarks acl = dfacl; 12761462Smarks } else { 12771462Smarks acl = normacl; 12781462Smarks } 12791462Smarks 12801462Smarks if ((acep->a_flags & ACE_OWNER)) { 12811462Smarks if (acl->state > ace_user_obj) { 12821462Smarks error = ENOTSUP; 12831462Smarks goto out; 12841462Smarks } 12851462Smarks acl->state = ace_user_obj; 12861462Smarks acl->seen |= USER_OBJ; 12871462Smarks vals = &acl->user_obj; 12881462Smarks vals->aent_type = USER_OBJ | acl->dfacl_flag; 12891462Smarks } else if ((acep->a_flags & ACE_EVERYONE)) { 12901462Smarks acl->state = ace_other_obj; 12911462Smarks acl->seen |= OTHER_OBJ; 12921462Smarks vals = &acl->other_obj; 12931462Smarks vals->aent_type = OTHER_OBJ | acl->dfacl_flag; 12941462Smarks } else if (acep->a_flags & ACE_IDENTIFIER_GROUP) { 12951462Smarks if (acl->state > ace_group) { 12961462Smarks error = ENOTSUP; 12971462Smarks goto out; 12981462Smarks } 12991462Smarks if ((acep->a_flags & ACE_GROUP)) { 13001462Smarks acl->seen |= GROUP_OBJ; 13011462Smarks vals = &acl->group_obj; 13021462Smarks vals->aent_type = GROUP_OBJ | acl->dfacl_flag; 13031462Smarks } else { 13041462Smarks acl->seen |= GROUP; 13051462Smarks vals = acevals_find(acep, &acl->group, 13061462Smarks &acl->numgroups); 13071462Smarks if (vals == NULL) { 13081462Smarks error = ENOMEM; 13091462Smarks goto out; 13101462Smarks } 13111462Smarks vals->aent_type = GROUP | acl->dfacl_flag; 13121462Smarks } 13131462Smarks acl->state = ace_group; 13141462Smarks } else { 13151462Smarks if (acl->state > ace_user) { 13161462Smarks error = ENOTSUP; 13171462Smarks goto out; 13181462Smarks } 13191462Smarks acl->state = ace_user; 13201462Smarks acl->seen |= USER; 13211462Smarks vals = acevals_find(acep, &acl->user, 13221462Smarks &acl->numusers); 13231462Smarks if (vals == NULL) { 13241462Smarks error = ENOMEM; 13251462Smarks goto out; 13261462Smarks } 13271462Smarks vals->aent_type = USER | acl->dfacl_flag; 13281462Smarks } 13291462Smarks 13301462Smarks if (!(acl->state > ace_unused)) { 13311462Smarks error = EINVAL; 13321462Smarks goto out; 13331462Smarks } 13341462Smarks 13351462Smarks if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { 13361462Smarks /* no more than one allowed per aclent_t */ 13371462Smarks if (vals->allowed != ACE_MASK_UNDEFINED) { 13381462Smarks error = ENOTSUP; 13391462Smarks goto out; 13401462Smarks } 13411462Smarks vals->allowed = acep->a_access_mask; 13421462Smarks } else { 13431462Smarks /* 13441462Smarks * it's a DENY; if there was a previous DENY, it 13451462Smarks * must have been an ACL_MASK. 13461462Smarks */ 13471462Smarks if (vals->denied != ACE_MASK_UNDEFINED) { 13481462Smarks /* ACL_MASK is for USER and GROUP only */ 13491462Smarks if ((acl->state != ace_user) && 13501462Smarks (acl->state != ace_group)) { 13511462Smarks error = ENOTSUP; 13521462Smarks goto out; 13531462Smarks } 13541462Smarks 13551462Smarks if (! acl->hasmask) { 13561462Smarks acl->hasmask = 1; 13571462Smarks acl->acl_mask = vals->denied; 13581462Smarks /* check for mismatched ACL_MASK emulations */ 13591462Smarks } else if (acl->acl_mask != vals->denied) { 13601462Smarks error = ENOTSUP; 13611462Smarks goto out; 13621462Smarks } 13631462Smarks vals->mask = vals->denied; 13641462Smarks } 13651462Smarks vals->denied = acep->a_access_mask; 13661462Smarks } 13671462Smarks } 13681462Smarks 13691462Smarks /* done collating; produce the aclent_t lists */ 13701462Smarks if (normacl->state != ace_unused) { 13711462Smarks error = ace_list_to_aent(normacl, aclentp, aclcnt, 13721462Smarks owner, group, isdir); 13731462Smarks if (error != 0) { 13741462Smarks goto out; 13751462Smarks } 13761462Smarks } 13771462Smarks if (dfacl->state != ace_unused) { 13781462Smarks error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt, 13791462Smarks owner, group, isdir); 13801462Smarks if (error != 0) { 13811462Smarks goto out; 13821462Smarks } 13831462Smarks } 13841462Smarks 13851462Smarks out: 13861462Smarks if (normacl != NULL) 13871462Smarks ace_list_free(normacl); 13881462Smarks if (dfacl != NULL) 13891462Smarks ace_list_free(dfacl); 13901462Smarks 13911462Smarks return (error); 13921462Smarks } 13931462Smarks 13941462Smarks static int 13951462Smarks convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir, 13961462Smarks uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt) 13971462Smarks { 13981462Smarks int error; 13991462Smarks aclent_t *aclentp, *dfaclentp; 14001462Smarks int aclcnt, dfaclcnt; 14011462Smarks 14021462Smarks error = ln_ace_to_aent(acebufp, acecnt, owner, group, 14031462Smarks &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir); 14041462Smarks 14051462Smarks if (error) 14061462Smarks return (error); 14071462Smarks 14081462Smarks 14091462Smarks if (dfaclcnt != 0) { 14101462Smarks /* 14111462Smarks * Slap aclentp and dfaclentp into a single array. 14121462Smarks */ 14131462Smarks aclentp = realloc(aclentp, (sizeof (aclent_t) * aclcnt) + 14141462Smarks (sizeof (aclent_t) * dfaclcnt)); 14151462Smarks if (aclentp != NULL) { 14161462Smarks (void) memcpy(aclentp + aclcnt, 14171462Smarks dfaclentp, sizeof (aclent_t) * dfaclcnt); 14181462Smarks } else { 14191462Smarks error = -1; 14201462Smarks } 14211462Smarks } 14221462Smarks 14231462Smarks if (aclentp) { 14241462Smarks *retaclentp = aclentp; 14251462Smarks *retaclcnt = aclcnt + dfaclcnt; 14261462Smarks } 14271462Smarks 14281462Smarks if (dfaclentp) 14291462Smarks free(dfaclentp); 14301462Smarks 14311462Smarks return (error); 14321462Smarks } 14331462Smarks 14341477Smarks static int 1435789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp) 1436789Sahrens { 1437789Sahrens const char *fname; 1438789Sahrens int fd; 1439789Sahrens int ace_acl = 0; 1440789Sahrens int error; 1441789Sahrens int getcmd, cntcmd; 1442789Sahrens acl_t *acl_info; 1443789Sahrens int save_errno; 1444789Sahrens int stat_error; 1445789Sahrens struct stat64 statbuf; 1446789Sahrens 1447789Sahrens *aclp = NULL; 1448789Sahrens if (type == ACL_PATH) { 1449789Sahrens fname = inp.file; 1450789Sahrens ace_acl = pathconf(fname, _PC_ACL_ENABLED); 1451789Sahrens } else { 1452789Sahrens fd = inp.fd; 1453789Sahrens ace_acl = fpathconf(fd, _PC_ACL_ENABLED); 1454789Sahrens } 1455789Sahrens 1456789Sahrens /* 1457789Sahrens * if acl's aren't supported then 1458789Sahrens * send it through the old GETACL interface 1459789Sahrens */ 14601666Smarks if (ace_acl == 0 || ace_acl == -1) { 1461789Sahrens ace_acl = _ACL_ACLENT_ENABLED; 1462789Sahrens } 1463789Sahrens 1464789Sahrens if (ace_acl & _ACL_ACE_ENABLED) { 1465789Sahrens cntcmd = ACE_GETACLCNT; 1466789Sahrens getcmd = ACE_GETACL; 1467789Sahrens acl_info = acl_alloc(ACE_T); 1468789Sahrens } else { 1469789Sahrens cntcmd = GETACLCNT; 1470789Sahrens getcmd = GETACL; 1471789Sahrens acl_info = acl_alloc(ACLENT_T); 1472789Sahrens } 1473789Sahrens 1474789Sahrens if (acl_info == NULL) 1475789Sahrens return (-1); 1476789Sahrens 1477789Sahrens if (type == ACL_PATH) { 1478789Sahrens acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL); 1479789Sahrens } else { 1480789Sahrens acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL); 1481789Sahrens } 1482789Sahrens 1483789Sahrens save_errno = errno; 1484789Sahrens if (acl_info->acl_cnt < 0) { 1485789Sahrens acl_free(acl_info); 1486789Sahrens errno = save_errno; 1487789Sahrens return (-1); 1488789Sahrens } 1489789Sahrens 1490789Sahrens if (acl_info->acl_cnt == 0) { 1491789Sahrens acl_free(acl_info); 1492789Sahrens errno = save_errno; 1493789Sahrens return (0); 1494789Sahrens } 1495789Sahrens 1496789Sahrens acl_info->acl_aclp = 1497789Sahrens malloc(acl_info->acl_cnt * acl_info->acl_entry_size); 1498789Sahrens save_errno = errno; 1499789Sahrens 1500789Sahrens if (acl_info->acl_aclp == NULL) { 1501789Sahrens acl_free(acl_info); 1502789Sahrens errno = save_errno; 1503789Sahrens return (-1); 1504789Sahrens } 1505789Sahrens 1506789Sahrens if (type == ACL_PATH) { 1507789Sahrens stat_error = stat64(fname, &statbuf); 1508789Sahrens error = acl(fname, getcmd, acl_info->acl_cnt, 1509789Sahrens acl_info->acl_aclp); 1510789Sahrens } else { 1511789Sahrens stat_error = fstat64(fd, &statbuf); 1512789Sahrens error = facl(fd, getcmd, acl_info->acl_cnt, 1513789Sahrens acl_info->acl_aclp); 1514789Sahrens } 1515789Sahrens 1516789Sahrens save_errno = errno; 1517789Sahrens if (error == -1) { 1518789Sahrens acl_free(acl_info); 1519789Sahrens errno = save_errno; 1520789Sahrens return (-1); 1521789Sahrens } 1522789Sahrens 1523789Sahrens 1524789Sahrens if (stat_error == 0) { 1525789Sahrens acl_info->acl_flags = 1526789Sahrens (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0); 1527789Sahrens } else 1528789Sahrens acl_info->acl_flags = 0; 1529789Sahrens 1530789Sahrens switch (acl_info->acl_type) { 1531789Sahrens case ACLENT_T: 1532789Sahrens if (acl_info->acl_cnt <= MIN_ACL_ENTRIES) 1533789Sahrens acl_info->acl_flags |= ACL_IS_TRIVIAL; 1534789Sahrens break; 1535789Sahrens case ACE_T: 1536789Sahrens if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0) 1537789Sahrens acl_info->acl_flags |= ACL_IS_TRIVIAL; 1538789Sahrens break; 1539789Sahrens default: 1540789Sahrens errno = EINVAL; 1541789Sahrens acl_free(acl_info); 1542789Sahrens return (-1); 1543789Sahrens } 1544789Sahrens 1545789Sahrens if ((acl_info->acl_flags & ACL_IS_TRIVIAL) && 1546789Sahrens (get_flag & ACL_NO_TRIVIAL)) { 1547789Sahrens acl_free(acl_info); 1548789Sahrens errno = 0; 1549789Sahrens return (0); 1550789Sahrens } 1551789Sahrens 1552789Sahrens *aclp = acl_info; 1553789Sahrens return (0); 1554789Sahrens } 1555789Sahrens 1556789Sahrens /* 1557789Sahrens * return -1 on failure, otherwise the number of acl 1558789Sahrens * entries is returned 1559789Sahrens */ 1560789Sahrens int 1561789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp) 1562789Sahrens { 1563789Sahrens acl_inp acl_inp; 1564789Sahrens acl_inp.file = path; 1565789Sahrens 1566789Sahrens return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp)); 1567789Sahrens } 1568789Sahrens 1569789Sahrens int 1570789Sahrens facl_get(int fd, int get_flag, acl_t **aclp) 1571789Sahrens { 1572789Sahrens 1573789Sahrens acl_inp acl_inp; 1574789Sahrens acl_inp.fd = fd; 1575789Sahrens 1576789Sahrens return (cacl_get(acl_inp, get_flag, ACL_FD, aclp)); 1577789Sahrens } 1578789Sahrens 15791462Smarks static int 15801462Smarks acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner, 15811462Smarks gid_t group) 15821462Smarks { 15831462Smarks int aclcnt; 15841462Smarks void *acldata; 15851462Smarks int error; 15861462Smarks 15871462Smarks /* 15881462Smarks * See if we need to translate 15891462Smarks */ 15901462Smarks if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) || 15911462Smarks (target_flavor == _ACL_ACLENT_ENABLED && 15921462Smarks aclp->acl_type == ACLENT_T)) 15931462Smarks return (0); 15941462Smarks 15951462Smarks if (target_flavor == -1) 15961462Smarks return (-1); 15971462Smarks 15981462Smarks if (target_flavor == _ACL_ACE_ENABLED && 15991462Smarks aclp->acl_type == ACLENT_T) { 16001462Smarks error = convert_aent_to_ace(aclp->acl_aclp, 16011462Smarks aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt); 16021462Smarks if (error) { 16031462Smarks errno = error; 16041462Smarks return (-1); 16051462Smarks } 16061462Smarks } else if (target_flavor == _ACL_ACLENT_ENABLED && 16071462Smarks aclp->acl_type == ACE_T) { 16081462Smarks error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt, 16091462Smarks isdir, owner, group, (aclent_t **)&acldata, &aclcnt); 16101462Smarks if (error) { 16111462Smarks errno = error; 16121462Smarks return (-1); 16131462Smarks } 16141462Smarks } else { 16151462Smarks errno = ENOTSUP; 16161462Smarks return (-1); 16171462Smarks } 16181462Smarks 16191462Smarks /* 16201462Smarks * replace old acl with newly translated acl 16211462Smarks */ 16221462Smarks free(aclp->acl_aclp); 16231462Smarks aclp->acl_aclp = acldata; 16241462Smarks aclp->acl_cnt = aclcnt; 16251462Smarks aclp->acl_type = (target_flavor == _ACL_ACE_ENABLED) ? ACE_T : ACLENT_T; 16261462Smarks return (0); 16271462Smarks } 16281462Smarks 1629789Sahrens /* 1630789Sahrens * Set an ACL, translates acl to ace_t when appropriate. 1631789Sahrens */ 1632789Sahrens static int 1633789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type) 1634789Sahrens { 1635789Sahrens int error = 0; 1636789Sahrens int acl_flavor_target; 1637789Sahrens struct stat64 statbuf; 1638789Sahrens int stat_error; 1639789Sahrens int isdir; 1640789Sahrens 1641789Sahrens 1642789Sahrens if (type == ACL_PATH) { 1643789Sahrens stat_error = stat64(acl_inp->file, &statbuf); 1644789Sahrens if (stat_error) 1645789Sahrens return (-1); 1646789Sahrens acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED); 1647789Sahrens } else { 1648789Sahrens stat_error = fstat64(acl_inp->fd, &statbuf); 1649789Sahrens if (stat_error) 1650789Sahrens return (-1); 1651789Sahrens acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED); 1652789Sahrens } 1653789Sahrens 16541666Smarks /* 16551666Smarks * If target returns an error or 0 from pathconf call then 16561666Smarks * fall back to UFS/POSIX Draft interface. 16571666Smarks * In the case of 0 we will then fail in either acl(2) or 16581666Smarks * acl_translate(). We could erroneously get 0 back from 16591666Smarks * a file system that is using fs_pathconf() and not answering 16601666Smarks * the _PC_ACL_ENABLED question itself. 16611666Smarks */ 16621666Smarks if (acl_flavor_target == 0 || acl_flavor_target == -1) 16631666Smarks acl_flavor_target = _ACL_ACLENT_ENABLED; 16641666Smarks 1665789Sahrens isdir = S_ISDIR(statbuf.st_mode); 1666789Sahrens 16671462Smarks if ((error = acl_translate(aclp, acl_flavor_target, isdir, 16681462Smarks statbuf.st_uid, statbuf.st_gid)) != 0) { 16691462Smarks return (error); 1670789Sahrens } 1671789Sahrens 1672789Sahrens if (type == ACL_PATH) { 1673789Sahrens error = acl(acl_inp->file, 1674789Sahrens (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, 1675789Sahrens aclp->acl_cnt, aclp->acl_aclp); 1676789Sahrens } else { 1677789Sahrens error = facl(acl_inp->fd, 1678789Sahrens (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, 1679789Sahrens aclp->acl_cnt, aclp->acl_aclp); 1680789Sahrens } 1681789Sahrens 1682789Sahrens return (error); 1683789Sahrens } 1684789Sahrens 1685789Sahrens int 1686789Sahrens acl_set(const char *path, acl_t *aclp) 1687789Sahrens { 1688789Sahrens acl_inp acl_inp; 1689789Sahrens 1690789Sahrens acl_inp.file = path; 1691789Sahrens 1692789Sahrens return (cacl_set(&acl_inp, aclp, ACL_PATH)); 1693789Sahrens } 1694789Sahrens 1695789Sahrens int 1696789Sahrens facl_set(int fd, acl_t *aclp) 1697789Sahrens { 1698789Sahrens acl_inp acl_inp; 1699789Sahrens 1700789Sahrens acl_inp.fd = fd; 1701789Sahrens 1702789Sahrens return (cacl_set(&acl_inp, aclp, ACL_FD)); 1703789Sahrens } 1704789Sahrens 1705789Sahrens int 1706789Sahrens acl_cnt(acl_t *aclp) 1707789Sahrens { 1708789Sahrens return (aclp->acl_cnt); 1709789Sahrens } 1710789Sahrens 1711789Sahrens int 1712789Sahrens acl_type(acl_t *aclp) 1713789Sahrens { 1714789Sahrens return (aclp->acl_type); 1715789Sahrens } 1716789Sahrens 1717789Sahrens acl_t * 1718789Sahrens acl_dup(acl_t *aclp) 1719789Sahrens { 1720789Sahrens acl_t *newaclp; 1721789Sahrens 1722789Sahrens newaclp = acl_alloc(aclp->acl_type); 1723789Sahrens if (newaclp == NULL) 1724789Sahrens return (NULL); 1725789Sahrens 1726789Sahrens newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt); 1727789Sahrens if (newaclp->acl_aclp == NULL) { 1728789Sahrens acl_free(newaclp); 1729789Sahrens return (NULL); 1730789Sahrens } 1731789Sahrens 1732789Sahrens (void) memcpy(newaclp->acl_aclp, 1733789Sahrens aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt); 1734789Sahrens newaclp->acl_cnt = aclp->acl_cnt; 1735789Sahrens 1736789Sahrens return (newaclp); 1737789Sahrens } 1738789Sahrens 1739789Sahrens int 1740789Sahrens acl_flags(acl_t *aclp) 1741789Sahrens { 1742789Sahrens return (aclp->acl_flags); 1743789Sahrens } 1744789Sahrens 1745789Sahrens void * 1746789Sahrens acl_data(acl_t *aclp) 1747789Sahrens { 1748789Sahrens return (aclp->acl_aclp); 1749789Sahrens } 1750789Sahrens 1751789Sahrens /* 1752*1953Smarks * Take an acl array and build an acl_t. 1753*1953Smarks */ 1754*1953Smarks acl_t * 1755*1953Smarks acl_to_aclp(enum acl_type type, void *acl, int count) 1756*1953Smarks { 1757*1953Smarks acl_t *aclp; 1758*1953Smarks 1759*1953Smarks 1760*1953Smarks aclp = acl_alloc(type); 1761*1953Smarks if (aclp == NULL) 1762*1953Smarks return (aclp); 1763*1953Smarks 1764*1953Smarks aclp->acl_aclp = acl; 1765*1953Smarks aclp->acl_cnt = count; 1766*1953Smarks 1767*1953Smarks return (aclp); 1768*1953Smarks } 1769*1953Smarks 1770*1953Smarks /* 1771789Sahrens * Remove an ACL from a file and create a trivial ACL based 1772789Sahrens * off of the mode argument. After acl has been set owner/group 1773789Sahrens * are updated to match owner,group arguments 1774789Sahrens */ 1775789Sahrens int 1776789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode) 1777789Sahrens { 1778789Sahrens int error = 0; 1779789Sahrens aclent_t min_acl[MIN_ACL_ENTRIES]; 1780789Sahrens ace_t min_ace_acl[6]; /* owner, group, everyone + complement denies */ 1781789Sahrens int acl_flavor; 1782789Sahrens int aclcnt; 1783789Sahrens 1784789Sahrens acl_flavor = pathconf(file, _PC_ACL_ENABLED); 1785789Sahrens 1786789Sahrens /* 1787789Sahrens * force it through aclent flavor when file system doesn't 1788789Sahrens * understand question 1789789Sahrens */ 17901666Smarks if (acl_flavor == 0 || acl_flavor == -1) 1791789Sahrens acl_flavor = _ACL_ACLENT_ENABLED; 1792789Sahrens 1793789Sahrens if (acl_flavor & _ACL_ACLENT_ENABLED) { 1794789Sahrens min_acl[0].a_type = USER_OBJ; 1795789Sahrens min_acl[0].a_id = owner; 1796789Sahrens min_acl[0].a_perm = ((mode & 0700) >> 6); 1797789Sahrens min_acl[1].a_type = GROUP_OBJ; 1798789Sahrens min_acl[1].a_id = group; 1799789Sahrens min_acl[1].a_perm = ((mode & 0070) >> 3); 1800789Sahrens min_acl[2].a_type = CLASS_OBJ; 1801789Sahrens min_acl[2].a_id = (uid_t)-1; 1802789Sahrens min_acl[2].a_perm = ((mode & 0070) >> 3); 1803789Sahrens min_acl[3].a_type = OTHER_OBJ; 1804789Sahrens min_acl[3].a_id = (uid_t)-1; 1805789Sahrens min_acl[3].a_perm = (mode & 0007); 1806789Sahrens aclcnt = 4; 1807789Sahrens error = acl(file, SETACL, aclcnt, min_acl); 1808789Sahrens } else if (acl_flavor & _ACL_ACE_ENABLED) { 1809789Sahrens (void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6); 1810789Sahrens 1811789Sahrens /* 1812789Sahrens * Make aces match request mode 1813789Sahrens */ 1814789Sahrens adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6); 1815789Sahrens adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3); 1816789Sahrens adjust_ace_pair(&min_ace_acl[4], mode & 0007); 1817789Sahrens 1818789Sahrens error = acl(file, ACE_SETACL, 6, min_ace_acl); 1819789Sahrens } else { 1820789Sahrens errno = EINVAL; 1821789Sahrens error = 1; 1822789Sahrens } 1823789Sahrens 1824789Sahrens if (error == 0) 1825789Sahrens error = chown(file, owner, group); 1826789Sahrens return (error); 1827789Sahrens } 1828789Sahrens 1829789Sahrens static int 1830789Sahrens ace_match(void *entry1, void *entry2) 1831789Sahrens { 1832789Sahrens ace_t *p1 = (ace_t *)entry1; 1833789Sahrens ace_t *p2 = (ace_t *)entry2; 1834789Sahrens ace_t ace1, ace2; 1835789Sahrens 1836789Sahrens ace1 = *p1; 1837789Sahrens ace2 = *p2; 1838789Sahrens 1839789Sahrens /* 1840789Sahrens * Need to fixup who field for abstrations for 1841789Sahrens * accurate comparison, since field is undefined. 1842789Sahrens */ 1843789Sahrens if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) 1844789Sahrens ace1.a_who = -1; 1845789Sahrens if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) 1846789Sahrens ace2.a_who = -1; 1847789Sahrens return (memcmp(&ace1, &ace2, sizeof (ace_t))); 1848789Sahrens } 1849789Sahrens 1850789Sahrens static int 1851789Sahrens aclent_match(void *entry1, void *entry2) 1852789Sahrens { 1853789Sahrens aclent_t *aclent1 = (aclent_t *)entry1; 1854789Sahrens aclent_t *aclent2 = (aclent_t *)entry2; 1855789Sahrens 1856789Sahrens return (memcmp(aclent1, aclent2, sizeof (aclent_t))); 1857789Sahrens } 1858789Sahrens 1859789Sahrens /* 1860789Sahrens * Find acl entries in acl that correspond to removeacl. Search 1861789Sahrens * is started from slot. The flag argument indicates whether to 1862789Sahrens * remove all matches or just the first match. 1863789Sahrens */ 1864789Sahrens int 1865789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag) 1866789Sahrens { 1867789Sahrens int i, j; 1868789Sahrens int match; 1869789Sahrens int (*acl_match)(void *acl1, void *acl2); 1870789Sahrens void *acl_entry, *remove_entry; 1871789Sahrens void *start; 1872789Sahrens int found = 0; 1873789Sahrens 1874789Sahrens if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST) 1875789Sahrens flag = ACL_REMOVE_FIRST; 1876789Sahrens 1877789Sahrens if (acl == NULL || removeacl == NULL) 1878789Sahrens return (EACL_NO_ACL_ENTRY); 1879789Sahrens 1880789Sahrens if (acl->acl_type != removeacl->acl_type) 1881789Sahrens return (EACL_DIFF_TYPE); 1882789Sahrens 1883789Sahrens if (acl->acl_type == ACLENT_T) 1884789Sahrens acl_match = aclent_match; 1885789Sahrens else 1886789Sahrens acl_match = ace_match; 1887789Sahrens 1888789Sahrens for (i = 0, remove_entry = removeacl->acl_aclp; 1889789Sahrens i != removeacl->acl_cnt; i++) { 1890789Sahrens 1891789Sahrens j = 0; 1892789Sahrens acl_entry = (char *)acl->acl_aclp + 1893789Sahrens (acl->acl_entry_size * start_slot); 1894789Sahrens for (;;) { 1895789Sahrens match = acl_match(acl_entry, remove_entry); 1896789Sahrens if (match == 0) { 1897789Sahrens found++; 1898789Sahrens start = (char *)acl_entry + 1899789Sahrens acl->acl_entry_size; 1900789Sahrens (void) memmove(acl_entry, start, 1901789Sahrens acl->acl_entry_size * 1902789Sahrens acl->acl_cnt-- - (j + 1)); 1903789Sahrens 1904789Sahrens if (flag == ACL_REMOVE_FIRST) 1905789Sahrens break; 1906789Sahrens /* 1907789Sahrens * List has changed, restart search from 1908789Sahrens * beginning. 1909789Sahrens */ 1910789Sahrens acl_entry = acl->acl_aclp; 1911789Sahrens j = 0; 1912789Sahrens continue; 1913789Sahrens } 1914789Sahrens acl_entry = ((char *)acl_entry + acl->acl_entry_size); 1915789Sahrens if (++j >= acl->acl_cnt) { 1916789Sahrens break; 1917789Sahrens } 1918789Sahrens } 1919789Sahrens } 1920789Sahrens 1921789Sahrens return ((found == 0) ? EACL_NO_ACL_ENTRY : 0); 1922789Sahrens } 1923789Sahrens 1924789Sahrens /* 1925789Sahrens * Replace entires entries in acl1 with the corresponding entries 1926789Sahrens * in newentries. The where argument specifies where to begin 1927789Sahrens * the replacement. If the where argument is 1 greater than the 1928789Sahrens * number of acl entries in acl1 then they are appended. If the 1929789Sahrens * where argument is 2+ greater than the number of acl entries then 1930789Sahrens * EACL_INVALID_SLOT is returned. 1931789Sahrens */ 1932789Sahrens int 1933789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where) 1934789Sahrens { 1935789Sahrens 1936789Sahrens int slot; 1937789Sahrens int slots_needed; 1938789Sahrens int slots_left; 1939789Sahrens int newsize; 1940789Sahrens 1941789Sahrens if (acl1 == NULL || newentries == NULL) 1942789Sahrens return (EACL_NO_ACL_ENTRY); 1943789Sahrens 1944789Sahrens if (where < 0 || where >= acl1->acl_cnt) 1945789Sahrens return (EACL_INVALID_SLOT); 1946789Sahrens 1947789Sahrens if (acl1->acl_type != newentries->acl_type) 1948789Sahrens return (EACL_DIFF_TYPE); 1949789Sahrens 1950789Sahrens slot = where; 1951789Sahrens 1952789Sahrens slots_left = acl1->acl_cnt - slot + 1; 1953789Sahrens if (slots_left < newentries->acl_cnt) { 1954789Sahrens slots_needed = newentries->acl_cnt - slots_left; 1955789Sahrens newsize = (acl1->acl_entry_size * acl1->acl_cnt) + 1956789Sahrens (acl1->acl_entry_size * slots_needed); 1957789Sahrens acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); 1958789Sahrens if (acl1->acl_aclp == NULL) 1959789Sahrens return (-1); 1960789Sahrens } 1961789Sahrens (void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot), 1962789Sahrens newentries->acl_aclp, 1963789Sahrens newentries->acl_entry_size * newentries->acl_cnt); 1964789Sahrens 1965789Sahrens /* 1966789Sahrens * Did ACL grow? 1967789Sahrens */ 1968789Sahrens 1969789Sahrens if ((slot + newentries->acl_cnt) > acl1->acl_cnt) { 1970789Sahrens acl1->acl_cnt = slot + newentries->acl_cnt; 1971789Sahrens } 1972789Sahrens 1973789Sahrens return (0); 1974789Sahrens } 1975789Sahrens 1976789Sahrens /* 1977789Sahrens * Add acl2 entries into acl1. The where argument specifies where 1978789Sahrens * to add the entries. 1979789Sahrens */ 1980789Sahrens int 1981789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where) 1982789Sahrens { 1983789Sahrens 1984789Sahrens int newsize; 1985789Sahrens int len; 1986789Sahrens void *start; 1987789Sahrens void *to; 1988789Sahrens 1989789Sahrens if (acl1 == NULL || acl2 == NULL) 1990789Sahrens return (EACL_NO_ACL_ENTRY); 1991789Sahrens 1992789Sahrens if (acl1->acl_type != acl2->acl_type) 1993789Sahrens return (EACL_DIFF_TYPE); 1994789Sahrens 1995789Sahrens /* 1996789Sahrens * allow where to specify 1 past last slot for an append operation 1997789Sahrens * but anything greater is an error. 1998789Sahrens */ 1999789Sahrens if (where < 0 || where > acl1->acl_cnt) 2000789Sahrens return (EACL_INVALID_SLOT); 2001789Sahrens 2002789Sahrens newsize = (acl2->acl_entry_size * acl2->acl_cnt) + 2003789Sahrens (acl1->acl_entry_size * acl1->acl_cnt); 2004789Sahrens acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); 2005789Sahrens if (acl1->acl_aclp == NULL) 2006789Sahrens return (-1); 2007789Sahrens 2008789Sahrens /* 2009789Sahrens * first push down entries where new ones will be inserted 2010789Sahrens */ 2011789Sahrens 2012789Sahrens to = (void *)((char *)acl1->acl_aclp + 2013789Sahrens ((where + acl2->acl_cnt) * acl1->acl_entry_size)); 2014789Sahrens 2015789Sahrens start = (void *)((char *)acl1->acl_aclp + 2016789Sahrens where * acl1->acl_entry_size); 2017789Sahrens 2018789Sahrens if (where < acl1->acl_cnt) { 2019789Sahrens len = (acl1->acl_cnt - where) * acl1->acl_entry_size; 2020789Sahrens (void) memmove(to, start, len); 2021789Sahrens } 2022789Sahrens 2023789Sahrens /* 2024789Sahrens * now stick in new entries. 2025789Sahrens */ 2026789Sahrens 2027789Sahrens (void) memmove(start, acl2->acl_aclp, 2028789Sahrens acl2->acl_cnt * acl2->acl_entry_size); 2029789Sahrens 2030789Sahrens acl1->acl_cnt += acl2->acl_cnt; 2031789Sahrens return (0); 2032789Sahrens } 2033789Sahrens 2034789Sahrens /* 2035789Sahrens * return text for an ACL error. 2036789Sahrens */ 2037789Sahrens char * 2038789Sahrens acl_strerror(int errnum) 2039789Sahrens { 2040789Sahrens switch (errnum) { 2041789Sahrens case EACL_GRP_ERROR: 2042789Sahrens return (dgettext(TEXT_DOMAIN, 20431420Smarks "There is more than one group or default group entry")); 2044789Sahrens case EACL_USER_ERROR: 2045789Sahrens return (dgettext(TEXT_DOMAIN, 20461420Smarks "There is more than one user or default user entry")); 2047789Sahrens case EACL_OTHER_ERROR: 2048789Sahrens return (dgettext(TEXT_DOMAIN, 2049789Sahrens "There is more than one other entry")); 2050789Sahrens case EACL_CLASS_ERROR: 2051789Sahrens return (dgettext(TEXT_DOMAIN, 2052789Sahrens "There is more than one mask entry")); 2053789Sahrens case EACL_DUPLICATE_ERROR: 2054789Sahrens return (dgettext(TEXT_DOMAIN, 2055789Sahrens "Duplicate user or group entries")); 2056789Sahrens case EACL_MISS_ERROR: 2057789Sahrens return (dgettext(TEXT_DOMAIN, 2058789Sahrens "Missing user/group owner, other, mask entry")); 2059789Sahrens case EACL_MEM_ERROR: 2060789Sahrens return (dgettext(TEXT_DOMAIN, 2061789Sahrens "Memory error")); 2062789Sahrens case EACL_ENTRY_ERROR: 2063789Sahrens return (dgettext(TEXT_DOMAIN, 2064789Sahrens "Unrecognized entry type")); 2065789Sahrens case EACL_INHERIT_ERROR: 2066789Sahrens return (dgettext(TEXT_DOMAIN, 2067789Sahrens "Invalid inheritance flags")); 2068789Sahrens case EACL_FLAGS_ERROR: 2069789Sahrens return (dgettext(TEXT_DOMAIN, 2070789Sahrens "Unrecognized entry flags")); 2071789Sahrens case EACL_PERM_MASK_ERROR: 2072789Sahrens return (dgettext(TEXT_DOMAIN, 2073789Sahrens "Invalid ACL permissions")); 2074789Sahrens case EACL_COUNT_ERROR: 2075789Sahrens return (dgettext(TEXT_DOMAIN, 2076789Sahrens "Invalid ACL count")); 2077789Sahrens case EACL_INVALID_SLOT: 2078789Sahrens return (dgettext(TEXT_DOMAIN, 2079789Sahrens "Invalid ACL entry number specified")); 2080789Sahrens case EACL_NO_ACL_ENTRY: 2081789Sahrens return (dgettext(TEXT_DOMAIN, 2082789Sahrens "ACL entry doesn't exist")); 2083789Sahrens case EACL_DIFF_TYPE: 2084789Sahrens return (dgettext(TEXT_DOMAIN, 2085789Sahrens "ACL type's are different")); 2086789Sahrens case EACL_INVALID_USER_GROUP: 2087789Sahrens return (dgettext(TEXT_DOMAIN, "Invalid user or group")); 2088789Sahrens case EACL_INVALID_STR: 2089789Sahrens return (dgettext(TEXT_DOMAIN, "ACL string is invalid")); 2090789Sahrens case EACL_FIELD_NOT_BLANK: 2091789Sahrens return (dgettext(TEXT_DOMAIN, "Field expected to be blank")); 2092789Sahrens case EACL_INVALID_ACCESS_TYPE: 2093789Sahrens return (dgettext(TEXT_DOMAIN, "Invalid access type")); 2094789Sahrens case EACL_UNKNOWN_DATA: 2095789Sahrens return (dgettext(TEXT_DOMAIN, "Unrecognized entry")); 2096789Sahrens case EACL_MISSING_FIELDS: 2097789Sahrens return (dgettext(TEXT_DOMAIN, 2098789Sahrens "ACL specification missing required fields")); 2099789Sahrens case EACL_INHERIT_NOTDIR: 2100789Sahrens return (dgettext(TEXT_DOMAIN, 2101789Sahrens "Inheritance flags are only allowed on directories")); 2102789Sahrens case -1: 2103789Sahrens return (strerror(errno)); 2104789Sahrens default: 2105789Sahrens errno = EINVAL; 2106789Sahrens return (dgettext(TEXT_DOMAIN, "Unknown error")); 2107789Sahrens } 2108789Sahrens } 21091420Smarks 21101420Smarks extern int yyinteractive; 21111420Smarks 21121420Smarks /* PRINTFLIKE1 */ 21131420Smarks void 21141420Smarks acl_error(const char *fmt, ...) 21151420Smarks { 21161420Smarks va_list va; 21171420Smarks 21181420Smarks if (yyinteractive == 0) 21191420Smarks return; 21201420Smarks 21211420Smarks va_start(va, fmt); 21221420Smarks (void) vfprintf(stderr, fmt, va); 21231420Smarks va_end(va); 21241420Smarks } 2125