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