1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 5789Sahrens * Common Development and Distribution License, Version 1.0 only 6789Sahrens * (the "License"). You may not use this file except in compliance 7789Sahrens * with the License. 8789Sahrens * 9789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10789Sahrens * or http://www.opensolaris.org/os/licensing. 11789Sahrens * See the License for the specific language governing permissions 12789Sahrens * and limitations under the License. 13789Sahrens * 14789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 15789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16789Sahrens * If applicable, add the following below this CDDL HEADER, with the 17789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 18789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 19789Sahrens * 20789Sahrens * CDDL HEADER END 21789Sahrens */ 22789Sahrens /* 23*1231Smarks * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28789Sahrens 29789Sahrens #include <stdlib.h> 30789Sahrens #include <string.h> 31789Sahrens #include <unistd.h> 32789Sahrens #include <limits.h> 33789Sahrens #include <grp.h> 34789Sahrens #include <pwd.h> 35789Sahrens #include <sys/types.h> 36789Sahrens #include <sys/acl.h> 37789Sahrens #include <errno.h> 38789Sahrens #include <sys/stat.h> 39789Sahrens #include <locale.h> 40789Sahrens #include <aclutils.h> 41789Sahrens #include <acl_common.h> 42789Sahrens 43789Sahrens #define ACL_PATH 0 44789Sahrens #define ACL_FD 1 45789Sahrens 46789Sahrens #define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \ 47789Sahrens ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \ 48789Sahrens ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL) 49789Sahrens 50789Sahrens 51789Sahrens #define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002 52789Sahrens #define ACL_SYNCHRONIZE_SET_DENY 0x0000001 53789Sahrens 54789Sahrens #define ACL_WRITE_OWNER_SET_ALLOW 0x0000020 55789Sahrens #define ACL_WRITE_OWNER_SET_DENY 0x0000010 56789Sahrens 57789Sahrens #define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 58789Sahrens #define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000 59789Sahrens 60789Sahrens #define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000 61789Sahrens 62789Sahrens #define ACL_DELETE_SET_ALLOW 0x0000200 63789Sahrens #define ACL_DELETE_SET_DENY 0x0000100 64789Sahrens 65789Sahrens #define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000 66789Sahrens 67789Sahrens #define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000 68789Sahrens #define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000 69789Sahrens 70789Sahrens #define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 71789Sahrens #define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000 72789Sahrens 73789Sahrens #define ACL_WRITE_OWNER_ERR_DENY 0x0000040 74789Sahrens #define ACL_READ_NAMED_READER_SET_DENY 0x1000000 75*1231Smarks 76789Sahrens typedef union { 77789Sahrens const char *file; 78789Sahrens int fd; 79789Sahrens } acl_inp; 80789Sahrens 81789Sahrens acl_t * 82789Sahrens acl_alloc(enum acl_type type) 83789Sahrens { 84789Sahrens acl_t *aclp; 85789Sahrens 86789Sahrens aclp = malloc(sizeof (acl_t)); 87789Sahrens 88789Sahrens if (aclp == NULL) 89789Sahrens return (NULL); 90789Sahrens 91789Sahrens aclp->acl_aclp = NULL; 92789Sahrens aclp->acl_cnt = 0; 93789Sahrens 94789Sahrens switch (type) { 95789Sahrens case ACE_T: 96789Sahrens aclp->acl_type = ACE_T; 97789Sahrens aclp->acl_entry_size = sizeof (ace_t); 98789Sahrens break; 99789Sahrens case ACLENT_T: 100789Sahrens aclp->acl_type = ACLENT_T; 101789Sahrens aclp->acl_entry_size = sizeof (aclent_t); 102789Sahrens break; 103789Sahrens default: 104789Sahrens acl_free(aclp); 105789Sahrens aclp = NULL; 106789Sahrens } 107789Sahrens return (aclp); 108789Sahrens } 109789Sahrens 110789Sahrens /* 111789Sahrens * Free acl_t structure 112789Sahrens */ 113789Sahrens void 114789Sahrens acl_free(acl_t *aclp) 115789Sahrens { 116789Sahrens if (aclp == NULL) 117789Sahrens return; 118789Sahrens 119789Sahrens if (aclp->acl_aclp) 120789Sahrens free(aclp->acl_aclp); 121789Sahrens free(aclp); 122789Sahrens } 123789Sahrens 124789Sahrens /* 125789Sahrens * Determine whether a file has a trivial ACL 126789Sahrens * returns: 0 = trivial 127789Sahrens * 1 = nontrivial 128789Sahrens * <0 some other system failure, such as ENOENT or EPERM 129789Sahrens */ 130789Sahrens int 131789Sahrens acl_trivial(const char *filename) 132789Sahrens { 133789Sahrens int acl_flavor; 134789Sahrens int aclcnt; 135789Sahrens int cntcmd; 136789Sahrens int val = 0; 137789Sahrens ace_t *acep; 138789Sahrens 139789Sahrens acl_flavor = pathconf(filename, _PC_ACL_ENABLED); 140789Sahrens if (acl_flavor == -1) 141789Sahrens return (-1); 142789Sahrens 143789Sahrens if (acl_flavor == _ACL_ACE_ENABLED) 144789Sahrens cntcmd = ACE_GETACLCNT; 145789Sahrens else 146789Sahrens cntcmd = GETACLCNT; 147789Sahrens 148789Sahrens aclcnt = acl(filename, cntcmd, 0, NULL); 149789Sahrens if (aclcnt > 0) { 150789Sahrens if (acl_flavor == _ACL_ACE_ENABLED) { 151*1231Smarks acep = malloc(sizeof (ace_t) * aclcnt); 152*1231Smarks if (acep == NULL) 153*1231Smarks return (-1); 154*1231Smarks if (acl(filename, ACE_GETACL, 155*1231Smarks aclcnt, acep) < 0) { 156*1231Smarks free(acep); 157*1231Smarks return (-1); 158*1231Smarks } 159789Sahrens 160*1231Smarks val = ace_trivial(acep, aclcnt); 161*1231Smarks free(acep); 162*1231Smarks 163789Sahrens } else if (aclcnt > MIN_ACL_ENTRIES) 164789Sahrens val = 1; 165789Sahrens } 166789Sahrens return (val); 167789Sahrens } 168789Sahrens 169789Sahrens static uint32_t 170789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow) 171789Sahrens { 172789Sahrens uint32_t access_mask = 0; 173789Sahrens int acl_produce; 174789Sahrens int synchronize_set = 0, write_owner_set = 0; 175789Sahrens int delete_set = 0, write_attrs_set = 0; 176789Sahrens int read_named_set = 0, write_named_set = 0; 177789Sahrens 178789Sahrens acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW | 179789Sahrens ACL_WRITE_ATTRS_OWNER_SET_ALLOW | 180789Sahrens ACL_WRITE_ATTRS_WRITER_SET_DENY); 181789Sahrens 182789Sahrens if (isallow) { 183789Sahrens synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW; 184789Sahrens write_owner_set = ACL_WRITE_OWNER_SET_ALLOW; 185789Sahrens delete_set = ACL_DELETE_SET_ALLOW; 186789Sahrens if (hasreadperm) 187789Sahrens read_named_set = ACL_READ_NAMED_READER_SET_ALLOW; 188789Sahrens if (haswriteperm) 189789Sahrens write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW; 190789Sahrens if (isowner) 191789Sahrens write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; 192789Sahrens else if (haswriteperm) 193789Sahrens write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; 194789Sahrens } else { 195789Sahrens 196789Sahrens synchronize_set = ACL_SYNCHRONIZE_SET_DENY; 197789Sahrens write_owner_set = ACL_WRITE_OWNER_SET_DENY; 198789Sahrens delete_set = ACL_DELETE_SET_DENY; 199789Sahrens if (hasreadperm) 200789Sahrens read_named_set = ACL_READ_NAMED_READER_SET_DENY; 201789Sahrens if (haswriteperm) 202789Sahrens write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY; 203789Sahrens if (isowner) 204789Sahrens write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY; 205789Sahrens else if (haswriteperm) 206789Sahrens write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY; 207789Sahrens else 208789Sahrens /* 209789Sahrens * If the entity is not the owner and does not 210789Sahrens * have write permissions ACE_WRITE_ATTRIBUTES will 211789Sahrens * always go in the DENY ACE. 212789Sahrens */ 213789Sahrens access_mask |= ACE_WRITE_ATTRIBUTES; 214789Sahrens } 215789Sahrens 216789Sahrens if (acl_produce & synchronize_set) 217789Sahrens access_mask |= ACE_SYNCHRONIZE; 218789Sahrens if (acl_produce & write_owner_set) 219789Sahrens access_mask |= ACE_WRITE_OWNER; 220789Sahrens if (acl_produce & delete_set) 221789Sahrens access_mask |= ACE_DELETE; 222789Sahrens if (acl_produce & write_attrs_set) 223789Sahrens access_mask |= ACE_WRITE_ATTRIBUTES; 224789Sahrens if (acl_produce & read_named_set) 225789Sahrens access_mask |= ACE_READ_NAMED_ATTRS; 226789Sahrens if (acl_produce & write_named_set) 227789Sahrens access_mask |= ACE_WRITE_NAMED_ATTRS; 228789Sahrens 229789Sahrens return (access_mask); 230789Sahrens } 231789Sahrens 232789Sahrens /* 233789Sahrens * Given an mode_t, convert it into an access_mask as used 234789Sahrens * by nfsace, assuming aclent_t -> nfsace semantics. 235789Sahrens */ 236789Sahrens static uint32_t 237789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow) 238789Sahrens { 239789Sahrens uint32_t access = 0; 240789Sahrens int haswriteperm = 0; 241789Sahrens int hasreadperm = 0; 242789Sahrens 243789Sahrens if (isallow) { 244789Sahrens haswriteperm = (mode & 02); 245789Sahrens hasreadperm = (mode & 04); 246789Sahrens } else { 247789Sahrens haswriteperm = !(mode & 02); 248789Sahrens hasreadperm = !(mode & 04); 249789Sahrens } 250789Sahrens 251789Sahrens /* 252789Sahrens * The following call takes care of correctly setting the following 253789Sahrens * mask bits in the access_mask: 254789Sahrens * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE, 255789Sahrens * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS 256789Sahrens */ 257789Sahrens access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow); 258789Sahrens 259789Sahrens if (isallow) { 260789Sahrens access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES; 261789Sahrens if (isowner) 262789Sahrens access |= ACE_WRITE_ACL; 263789Sahrens } else { 264789Sahrens if (! isowner) 265789Sahrens access |= ACE_WRITE_ACL; 266789Sahrens } 267789Sahrens 268789Sahrens /* read */ 269789Sahrens if (mode & 04) { 270789Sahrens access |= ACE_READ_DATA; 271789Sahrens } 272789Sahrens /* write */ 273789Sahrens if (mode & 02) { 274789Sahrens access |= ACE_WRITE_DATA | 275789Sahrens ACE_APPEND_DATA; 276789Sahrens if (isdir) 277789Sahrens access |= ACE_DELETE_CHILD; 278789Sahrens } 279789Sahrens /* exec */ 280789Sahrens if (mode & 01) { 281789Sahrens access |= ACE_EXECUTE; 282789Sahrens } 283789Sahrens 284789Sahrens return (access); 285789Sahrens } 286789Sahrens 287789Sahrens /* 288789Sahrens * Given an nfsace (presumably an ALLOW entry), make a 289789Sahrens * corresponding DENY entry at the address given. 290789Sahrens */ 291789Sahrens static void 292789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner) 293789Sahrens { 294789Sahrens (void) memcpy(deny, allow, sizeof (ace_t)); 295789Sahrens 296789Sahrens deny->a_who = allow->a_who; 297789Sahrens 298789Sahrens deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE; 299789Sahrens deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS; 300789Sahrens if (isdir) 301789Sahrens deny->a_access_mask ^= ACE_DELETE_CHILD; 302789Sahrens 303789Sahrens deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER | 304789Sahrens ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | 305789Sahrens ACE_WRITE_NAMED_ATTRS); 306789Sahrens deny->a_access_mask |= access_mask_set((allow->a_access_mask & 307789Sahrens ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner, 308789Sahrens B_FALSE); 309789Sahrens } 310789Sahrens /* 311789Sahrens * Make an initial pass over an array of aclent_t's. Gather 312789Sahrens * information such as an ACL_MASK (if any), number of users, 313789Sahrens * number of groups, and whether the array needs to be sorted. 314789Sahrens */ 315789Sahrens static int 316789Sahrens ln_aent_preprocess(aclent_t *aclent, int n, 317789Sahrens int *hasmask, mode_t *mask, 318789Sahrens int *numuser, int *numgroup, int *needsort) 319789Sahrens { 320789Sahrens int error = 0; 321789Sahrens int i; 322789Sahrens int curtype = 0; 323789Sahrens 324789Sahrens *hasmask = 0; 325789Sahrens *mask = 07; 326789Sahrens *needsort = 0; 327789Sahrens *numuser = 0; 328789Sahrens *numgroup = 0; 329789Sahrens 330789Sahrens for (i = 0; i < n; i++) { 331789Sahrens if (aclent[i].a_type < curtype) 332789Sahrens *needsort = 1; 333789Sahrens else if (aclent[i].a_type > curtype) 334789Sahrens curtype = aclent[i].a_type; 335789Sahrens if (aclent[i].a_type & USER) 336789Sahrens (*numuser)++; 337789Sahrens if (aclent[i].a_type & (GROUP | GROUP_OBJ)) 338789Sahrens (*numgroup)++; 339789Sahrens if (aclent[i].a_type & CLASS_OBJ) { 340789Sahrens if (*hasmask) { 341789Sahrens error = EINVAL; 342789Sahrens goto out; 343789Sahrens } else { 344789Sahrens *hasmask = 1; 345789Sahrens *mask = aclent[i].a_perm; 346789Sahrens } 347789Sahrens } 348789Sahrens } 349789Sahrens 350789Sahrens if ((! *hasmask) && (*numuser + *numgroup > 1)) { 351789Sahrens error = EINVAL; 352789Sahrens goto out; 353789Sahrens } 354789Sahrens 355789Sahrens out: 356789Sahrens return (error); 357789Sahrens } 358789Sahrens 359789Sahrens /* 360789Sahrens * Convert an array of aclent_t into an array of nfsace entries, 361789Sahrens * following POSIX draft -> nfsv4 conversion semantics as outlined in 362789Sahrens * the IETF draft. 363789Sahrens */ 364789Sahrens static int 365789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir) 366789Sahrens { 367789Sahrens int error = 0; 368789Sahrens mode_t mask; 369789Sahrens int numuser, numgroup, needsort; 370789Sahrens int resultsize = 0; 371789Sahrens int i, groupi = 0, skip; 372789Sahrens ace_t *acep, *result = NULL; 373789Sahrens int hasmask; 374789Sahrens 375789Sahrens error = ln_aent_preprocess(aclent, n, &hasmask, &mask, 376789Sahrens &numuser, &numgroup, &needsort); 377789Sahrens if (error != 0) 378789Sahrens goto out; 379789Sahrens 380789Sahrens /* allow + deny for each aclent */ 381789Sahrens resultsize = n * 2; 382789Sahrens if (hasmask) { 383789Sahrens /* 384789Sahrens * stick extra deny on the group_obj and on each 385789Sahrens * user|group for the mask (the group_obj was added 386789Sahrens * into the count for numgroup) 387789Sahrens */ 388789Sahrens resultsize += numuser + numgroup; 389789Sahrens /* ... and don't count the mask itself */ 390789Sahrens resultsize -= 2; 391789Sahrens } 392789Sahrens 393789Sahrens /* sort the source if necessary */ 394789Sahrens if (needsort) 395789Sahrens ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls); 396789Sahrens 397789Sahrens result = acep = calloc(1, resultsize * sizeof (ace_t)); 398789Sahrens if (result == NULL) 399789Sahrens goto out; 400789Sahrens 401789Sahrens for (i = 0; i < n; i++) { 402789Sahrens /* 403789Sahrens * don't process CLASS_OBJ (mask); mask was grabbed in 404789Sahrens * ln_aent_preprocess() 405789Sahrens */ 406789Sahrens if (aclent[i].a_type & CLASS_OBJ) 407789Sahrens continue; 408789Sahrens 409789Sahrens /* If we need an ACL_MASK emulator, prepend it now */ 410789Sahrens if ((hasmask) && 411789Sahrens (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) { 412789Sahrens acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE; 413789Sahrens acep->a_flags = 0; 414789Sahrens if (aclent[i].a_type & GROUP_OBJ) { 415789Sahrens acep->a_who = -1; 416789Sahrens acep->a_flags |= 417789Sahrens (ACE_IDENTIFIER_GROUP|ACE_GROUP); 418789Sahrens } else if (aclent[i].a_type & USER) { 419789Sahrens acep->a_who = aclent[i].a_id; 420789Sahrens } else { 421789Sahrens acep->a_who = aclent[i].a_id; 422789Sahrens acep->a_flags |= ACE_IDENTIFIER_GROUP; 423789Sahrens } 424789Sahrens if (aclent[i].a_type & ACL_DEFAULT) { 425789Sahrens acep->a_flags |= ACE_INHERIT_ONLY_ACE | 426789Sahrens ACE_FILE_INHERIT_ACE | 427789Sahrens ACE_DIRECTORY_INHERIT_ACE; 428789Sahrens } 429789Sahrens /* 430789Sahrens * Set the access mask for the prepended deny 431789Sahrens * ace. To do this, we invert the mask (found 432789Sahrens * in ln_aent_preprocess()) then convert it to an 433789Sahrens * DENY ace access_mask. 434789Sahrens */ 435789Sahrens acep->a_access_mask = mode_to_ace_access((mask ^ 07), 436789Sahrens isdir, 0, 0); 437789Sahrens acep += 1; 438789Sahrens } 439789Sahrens 440789Sahrens /* handle a_perm -> access_mask */ 441789Sahrens acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm, 442789Sahrens isdir, aclent[i].a_type & USER_OBJ, 1); 443789Sahrens 444789Sahrens /* emulate a default aclent */ 445789Sahrens if (aclent[i].a_type & ACL_DEFAULT) { 446789Sahrens acep->a_flags |= ACE_INHERIT_ONLY_ACE | 447789Sahrens ACE_FILE_INHERIT_ACE | 448789Sahrens ACE_DIRECTORY_INHERIT_ACE; 449789Sahrens } 450789Sahrens 451789Sahrens /* 452789Sahrens * handle a_perm and a_id 453789Sahrens * 454789Sahrens * this must be done last, since it involves the 455789Sahrens * corresponding deny aces, which are handled 456789Sahrens * differently for each different a_type. 457789Sahrens */ 458789Sahrens if (aclent[i].a_type & USER_OBJ) { 459789Sahrens acep->a_who = -1; 460789Sahrens acep->a_flags |= ACE_OWNER; 461789Sahrens ace_make_deny(acep, acep + 1, isdir, B_TRUE); 462789Sahrens acep += 2; 463789Sahrens } else if (aclent[i].a_type & USER) { 464789Sahrens acep->a_who = aclent[i].a_id; 465789Sahrens ace_make_deny(acep, acep + 1, isdir, B_FALSE); 466789Sahrens acep += 2; 467789Sahrens } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) { 468789Sahrens if (aclent[i].a_type & GROUP_OBJ) { 469789Sahrens acep->a_who = -1; 470789Sahrens acep->a_flags |= ACE_GROUP; 471789Sahrens } else { 472789Sahrens acep->a_who = aclent[i].a_id; 473789Sahrens } 474789Sahrens acep->a_flags |= ACE_IDENTIFIER_GROUP; 475789Sahrens /* 476789Sahrens * Set the corresponding deny for the group ace. 477789Sahrens * 478789Sahrens * The deny aces go after all of the groups, unlike 479789Sahrens * everything else, where they immediately follow 480789Sahrens * the allow ace. 481789Sahrens * 482789Sahrens * We calculate "skip", the number of slots to 483789Sahrens * skip ahead for the deny ace, here. 484789Sahrens * 485789Sahrens * The pattern is: 486789Sahrens * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3 487789Sahrens * thus, skip is 488789Sahrens * (2 * numgroup) - 1 - groupi 489789Sahrens * (2 * numgroup) to account for MD + A 490789Sahrens * - 1 to account for the fact that we're on the 491789Sahrens * access (A), not the mask (MD) 492789Sahrens * - groupi to account for the fact that we have 493789Sahrens * passed up groupi number of MD's. 494789Sahrens */ 495789Sahrens skip = (2 * numgroup) - 1 - groupi; 496789Sahrens ace_make_deny(acep, acep + skip, isdir, B_FALSE); 497789Sahrens /* 498789Sahrens * If we just did the last group, skip acep past 499789Sahrens * all of the denies; else, just move ahead one. 500789Sahrens */ 501789Sahrens if (++groupi >= numgroup) 502789Sahrens acep += numgroup + 1; 503789Sahrens else 504789Sahrens acep += 1; 505789Sahrens } else if (aclent[i].a_type & OTHER_OBJ) { 506789Sahrens acep->a_who = -1; 507789Sahrens acep->a_flags |= ACE_EVERYONE; 508789Sahrens ace_make_deny(acep, acep + 1, isdir, B_FALSE); 509789Sahrens acep += 2; 510789Sahrens } else { 511789Sahrens error = EINVAL; 512789Sahrens goto out; 513789Sahrens } 514789Sahrens } 515789Sahrens 516789Sahrens *acepp = result; 517789Sahrens *rescount = resultsize; 518789Sahrens 519789Sahrens out: 520789Sahrens if (error != 0) { 521789Sahrens if ((result != NULL) && (resultsize > 0)) { 522789Sahrens free(result); 523789Sahrens } 524789Sahrens } 525789Sahrens 526789Sahrens return (error); 527789Sahrens } 528789Sahrens 529789Sahrens static int 530789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir, 531789Sahrens ace_t **retacep, int *retacecnt) 532789Sahrens { 533789Sahrens ace_t *acep; 534789Sahrens ace_t *dfacep; 535789Sahrens ace_t *newacep; 536789Sahrens int acecnt = 0; 537789Sahrens int dfacecnt = 0; 538789Sahrens int dfaclstart = 0; 539789Sahrens int dfaclcnt = 0; 540789Sahrens aclent_t *aclp; 541789Sahrens int i; 542789Sahrens int error; 543789Sahrens 544789Sahrens ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls); 545789Sahrens 546789Sahrens for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) { 547789Sahrens if (aclp->a_type & ACL_DEFAULT) 548789Sahrens break; 549789Sahrens } 550789Sahrens 551789Sahrens if (i < aclcnt) { 552789Sahrens dfaclstart = aclcnt - i; 553789Sahrens dfaclcnt = i; 554789Sahrens } 555789Sahrens 556789Sahrens if (dfaclcnt && isdir == 0) { 557789Sahrens return (-1); 558789Sahrens } 559789Sahrens 560789Sahrens error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir); 561789Sahrens if (error) 562789Sahrens return (-1); 563789Sahrens 564789Sahrens if (dfaclcnt) { 565789Sahrens error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt, 566789Sahrens &dfacep, &dfacecnt, isdir); 567789Sahrens if (error) { 568789Sahrens if (acep) { 569789Sahrens free(acep); 570789Sahrens } 571789Sahrens return (-1); 572789Sahrens } 573789Sahrens } 574789Sahrens 575789Sahrens newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt)); 576789Sahrens if (newacep == NULL) 577789Sahrens return (-1); 578789Sahrens 579789Sahrens (void) memcpy(newacep, acep, sizeof (ace_t) * acecnt); 580789Sahrens if (dfaclcnt) { 581789Sahrens (void) memcpy(newacep + acecnt, dfacep, 582789Sahrens sizeof (ace_t) * dfacecnt); 583789Sahrens } 584789Sahrens free(acep); 585789Sahrens if (dfaclcnt) 586789Sahrens free(dfacep); 587789Sahrens 588789Sahrens *retacecnt = acecnt + dfacecnt; 589789Sahrens *retacep = newacep; 590789Sahrens return (0); 591789Sahrens } 592789Sahrens 593789Sahrens 594789Sahrens static int 595789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp) 596789Sahrens { 597789Sahrens const char *fname; 598789Sahrens int fd; 599789Sahrens int ace_acl = 0; 600789Sahrens int error; 601789Sahrens int getcmd, cntcmd; 602789Sahrens acl_t *acl_info; 603789Sahrens int save_errno; 604789Sahrens int stat_error; 605789Sahrens struct stat64 statbuf; 606789Sahrens 607789Sahrens *aclp = NULL; 608789Sahrens if (type == ACL_PATH) { 609789Sahrens fname = inp.file; 610789Sahrens ace_acl = pathconf(fname, _PC_ACL_ENABLED); 611789Sahrens } else { 612789Sahrens fd = inp.fd; 613789Sahrens ace_acl = fpathconf(fd, _PC_ACL_ENABLED); 614789Sahrens } 615789Sahrens 616789Sahrens if (ace_acl == -1) 617789Sahrens return (-1); 618789Sahrens 619789Sahrens /* 620789Sahrens * if acl's aren't supported then 621789Sahrens * send it through the old GETACL interface 622789Sahrens */ 623789Sahrens if (ace_acl == 0) { 624789Sahrens ace_acl = _ACL_ACLENT_ENABLED; 625789Sahrens } 626789Sahrens 627789Sahrens if (ace_acl & _ACL_ACE_ENABLED) { 628789Sahrens cntcmd = ACE_GETACLCNT; 629789Sahrens getcmd = ACE_GETACL; 630789Sahrens acl_info = acl_alloc(ACE_T); 631789Sahrens } else { 632789Sahrens cntcmd = GETACLCNT; 633789Sahrens getcmd = GETACL; 634789Sahrens acl_info = acl_alloc(ACLENT_T); 635789Sahrens } 636789Sahrens 637789Sahrens if (acl_info == NULL) 638789Sahrens return (-1); 639789Sahrens 640789Sahrens if (type == ACL_PATH) { 641789Sahrens acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL); 642789Sahrens } else { 643789Sahrens acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL); 644789Sahrens } 645789Sahrens 646789Sahrens save_errno = errno; 647789Sahrens if (acl_info->acl_cnt < 0) { 648789Sahrens acl_free(acl_info); 649789Sahrens errno = save_errno; 650789Sahrens return (-1); 651789Sahrens } 652789Sahrens 653789Sahrens if (acl_info->acl_cnt == 0) { 654789Sahrens acl_free(acl_info); 655789Sahrens errno = save_errno; 656789Sahrens return (0); 657789Sahrens } 658789Sahrens 659789Sahrens acl_info->acl_aclp = 660789Sahrens malloc(acl_info->acl_cnt * acl_info->acl_entry_size); 661789Sahrens save_errno = errno; 662789Sahrens 663789Sahrens if (acl_info->acl_aclp == NULL) { 664789Sahrens acl_free(acl_info); 665789Sahrens errno = save_errno; 666789Sahrens return (-1); 667789Sahrens } 668789Sahrens 669789Sahrens if (type == ACL_PATH) { 670789Sahrens stat_error = stat64(fname, &statbuf); 671789Sahrens error = acl(fname, getcmd, acl_info->acl_cnt, 672789Sahrens acl_info->acl_aclp); 673789Sahrens } else { 674789Sahrens stat_error = fstat64(fd, &statbuf); 675789Sahrens error = facl(fd, getcmd, acl_info->acl_cnt, 676789Sahrens acl_info->acl_aclp); 677789Sahrens } 678789Sahrens 679789Sahrens save_errno = errno; 680789Sahrens if (error == -1) { 681789Sahrens acl_free(acl_info); 682789Sahrens errno = save_errno; 683789Sahrens return (-1); 684789Sahrens } 685789Sahrens 686789Sahrens 687789Sahrens if (stat_error == 0) { 688789Sahrens acl_info->acl_flags = 689789Sahrens (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0); 690789Sahrens } else 691789Sahrens acl_info->acl_flags = 0; 692789Sahrens 693789Sahrens switch (acl_info->acl_type) { 694789Sahrens case ACLENT_T: 695789Sahrens if (acl_info->acl_cnt <= MIN_ACL_ENTRIES) 696789Sahrens acl_info->acl_flags |= ACL_IS_TRIVIAL; 697789Sahrens break; 698789Sahrens case ACE_T: 699789Sahrens if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0) 700789Sahrens acl_info->acl_flags |= ACL_IS_TRIVIAL; 701789Sahrens break; 702789Sahrens default: 703789Sahrens errno = EINVAL; 704789Sahrens acl_free(acl_info); 705789Sahrens return (-1); 706789Sahrens } 707789Sahrens 708789Sahrens if ((acl_info->acl_flags & ACL_IS_TRIVIAL) && 709789Sahrens (get_flag & ACL_NO_TRIVIAL)) { 710789Sahrens acl_free(acl_info); 711789Sahrens errno = 0; 712789Sahrens return (0); 713789Sahrens } 714789Sahrens 715789Sahrens *aclp = acl_info; 716789Sahrens return (0); 717789Sahrens } 718789Sahrens 719789Sahrens /* 720789Sahrens * return -1 on failure, otherwise the number of acl 721789Sahrens * entries is returned 722789Sahrens */ 723789Sahrens int 724789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp) 725789Sahrens { 726789Sahrens acl_inp acl_inp; 727789Sahrens acl_inp.file = path; 728789Sahrens 729789Sahrens return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp)); 730789Sahrens } 731789Sahrens 732789Sahrens int 733789Sahrens facl_get(int fd, int get_flag, acl_t **aclp) 734789Sahrens { 735789Sahrens 736789Sahrens acl_inp acl_inp; 737789Sahrens acl_inp.fd = fd; 738789Sahrens 739789Sahrens return (cacl_get(acl_inp, get_flag, ACL_FD, aclp)); 740789Sahrens } 741789Sahrens 742789Sahrens /* 743789Sahrens * Set an ACL, translates acl to ace_t when appropriate. 744789Sahrens */ 745789Sahrens static int 746789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type) 747789Sahrens { 748789Sahrens int error = 0; 749789Sahrens int acl_flavor_target; 750789Sahrens ace_t *acep = NULL; 751789Sahrens int acecnt; 752789Sahrens struct stat64 statbuf; 753789Sahrens int stat_error; 754789Sahrens int isdir; 755789Sahrens 756789Sahrens 757789Sahrens if (type == ACL_PATH) { 758789Sahrens stat_error = stat64(acl_inp->file, &statbuf); 759789Sahrens if (stat_error) 760789Sahrens return (-1); 761789Sahrens acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED); 762789Sahrens } else { 763789Sahrens stat_error = fstat64(acl_inp->fd, &statbuf); 764789Sahrens if (stat_error) 765789Sahrens return (-1); 766789Sahrens acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED); 767789Sahrens } 768789Sahrens 769789Sahrens isdir = S_ISDIR(statbuf.st_mode); 770789Sahrens 771789Sahrens if (acl_flavor_target == -1) 772789Sahrens return (-1); 773789Sahrens 774789Sahrens /* 775789Sahrens * Translate aclent_t ACL's to ACE ACL's. 776789Sahrens */ 777789Sahrens if (acl_flavor_target == _ACL_ACE_ENABLED && 778789Sahrens aclp->acl_type == ACLENT_T) { 779789Sahrens error = convert_aent_to_ace(aclp->acl_aclp, 780789Sahrens aclp->acl_cnt, isdir, &acep, &acecnt); 781789Sahrens if (error) { 782789Sahrens errno = ENOTSUP; 783789Sahrens return (-1); 784789Sahrens } 785789Sahrens /* 786789Sahrens * replace old acl with newly translated acl 787789Sahrens */ 788789Sahrens free(aclp->acl_aclp); 789789Sahrens aclp->acl_aclp = acep; 790789Sahrens aclp->acl_cnt = acecnt; 791789Sahrens aclp->acl_type = ACE_T; 792789Sahrens } 793789Sahrens 794789Sahrens if (type == ACL_PATH) { 795789Sahrens error = acl(acl_inp->file, 796789Sahrens (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, 797789Sahrens aclp->acl_cnt, aclp->acl_aclp); 798789Sahrens } else { 799789Sahrens error = facl(acl_inp->fd, 800789Sahrens (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, 801789Sahrens aclp->acl_cnt, aclp->acl_aclp); 802789Sahrens } 803789Sahrens 804789Sahrens return (error); 805789Sahrens } 806789Sahrens 807789Sahrens int 808789Sahrens acl_set(const char *path, acl_t *aclp) 809789Sahrens { 810789Sahrens acl_inp acl_inp; 811789Sahrens 812789Sahrens acl_inp.file = path; 813789Sahrens 814789Sahrens return (cacl_set(&acl_inp, aclp, ACL_PATH)); 815789Sahrens } 816789Sahrens 817789Sahrens int 818789Sahrens facl_set(int fd, acl_t *aclp) 819789Sahrens { 820789Sahrens acl_inp acl_inp; 821789Sahrens 822789Sahrens acl_inp.fd = fd; 823789Sahrens 824789Sahrens return (cacl_set(&acl_inp, aclp, ACL_FD)); 825789Sahrens } 826789Sahrens 827789Sahrens int 828789Sahrens acl_cnt(acl_t *aclp) 829789Sahrens { 830789Sahrens return (aclp->acl_cnt); 831789Sahrens } 832789Sahrens 833789Sahrens int 834789Sahrens acl_type(acl_t *aclp) 835789Sahrens { 836789Sahrens return (aclp->acl_type); 837789Sahrens } 838789Sahrens 839789Sahrens acl_t * 840789Sahrens acl_dup(acl_t *aclp) 841789Sahrens { 842789Sahrens acl_t *newaclp; 843789Sahrens 844789Sahrens newaclp = acl_alloc(aclp->acl_type); 845789Sahrens if (newaclp == NULL) 846789Sahrens return (NULL); 847789Sahrens 848789Sahrens newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt); 849789Sahrens if (newaclp->acl_aclp == NULL) { 850789Sahrens acl_free(newaclp); 851789Sahrens return (NULL); 852789Sahrens } 853789Sahrens 854789Sahrens (void) memcpy(newaclp->acl_aclp, 855789Sahrens aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt); 856789Sahrens newaclp->acl_cnt = aclp->acl_cnt; 857789Sahrens 858789Sahrens return (newaclp); 859789Sahrens } 860789Sahrens 861789Sahrens int 862789Sahrens acl_flags(acl_t *aclp) 863789Sahrens { 864789Sahrens return (aclp->acl_flags); 865789Sahrens } 866789Sahrens 867789Sahrens void * 868789Sahrens acl_data(acl_t *aclp) 869789Sahrens { 870789Sahrens return (aclp->acl_aclp); 871789Sahrens } 872789Sahrens 873789Sahrens /* 874789Sahrens * Remove an ACL from a file and create a trivial ACL based 875789Sahrens * off of the mode argument. After acl has been set owner/group 876789Sahrens * are updated to match owner,group arguments 877789Sahrens */ 878789Sahrens int 879789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode) 880789Sahrens { 881789Sahrens int error = 0; 882789Sahrens aclent_t min_acl[MIN_ACL_ENTRIES]; 883789Sahrens ace_t min_ace_acl[6]; /* owner, group, everyone + complement denies */ 884789Sahrens int acl_flavor; 885789Sahrens int aclcnt; 886789Sahrens 887789Sahrens acl_flavor = pathconf(file, _PC_ACL_ENABLED); 888789Sahrens 889789Sahrens if (acl_flavor == -1) 890789Sahrens return (-1); 891789Sahrens /* 892789Sahrens * force it through aclent flavor when file system doesn't 893789Sahrens * understand question 894789Sahrens */ 895789Sahrens if (acl_flavor == 0) 896789Sahrens acl_flavor = _ACL_ACLENT_ENABLED; 897789Sahrens 898789Sahrens if (acl_flavor & _ACL_ACLENT_ENABLED) { 899789Sahrens min_acl[0].a_type = USER_OBJ; 900789Sahrens min_acl[0].a_id = owner; 901789Sahrens min_acl[0].a_perm = ((mode & 0700) >> 6); 902789Sahrens min_acl[1].a_type = GROUP_OBJ; 903789Sahrens min_acl[1].a_id = group; 904789Sahrens min_acl[1].a_perm = ((mode & 0070) >> 3); 905789Sahrens min_acl[2].a_type = CLASS_OBJ; 906789Sahrens min_acl[2].a_id = (uid_t)-1; 907789Sahrens min_acl[2].a_perm = ((mode & 0070) >> 3); 908789Sahrens min_acl[3].a_type = OTHER_OBJ; 909789Sahrens min_acl[3].a_id = (uid_t)-1; 910789Sahrens min_acl[3].a_perm = (mode & 0007); 911789Sahrens aclcnt = 4; 912789Sahrens error = acl(file, SETACL, aclcnt, min_acl); 913789Sahrens } else if (acl_flavor & _ACL_ACE_ENABLED) { 914789Sahrens (void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6); 915789Sahrens 916789Sahrens /* 917789Sahrens * Make aces match request mode 918789Sahrens */ 919789Sahrens adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6); 920789Sahrens adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3); 921789Sahrens adjust_ace_pair(&min_ace_acl[4], mode & 0007); 922789Sahrens 923789Sahrens error = acl(file, ACE_SETACL, 6, min_ace_acl); 924789Sahrens } else { 925789Sahrens errno = EINVAL; 926789Sahrens error = 1; 927789Sahrens } 928789Sahrens 929789Sahrens if (error == 0) 930789Sahrens error = chown(file, owner, group); 931789Sahrens return (error); 932789Sahrens } 933789Sahrens 934789Sahrens static int 935789Sahrens ace_match(void *entry1, void *entry2) 936789Sahrens { 937789Sahrens ace_t *p1 = (ace_t *)entry1; 938789Sahrens ace_t *p2 = (ace_t *)entry2; 939789Sahrens ace_t ace1, ace2; 940789Sahrens 941789Sahrens ace1 = *p1; 942789Sahrens ace2 = *p2; 943789Sahrens 944789Sahrens /* 945789Sahrens * Need to fixup who field for abstrations for 946789Sahrens * accurate comparison, since field is undefined. 947789Sahrens */ 948789Sahrens if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) 949789Sahrens ace1.a_who = -1; 950789Sahrens if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) 951789Sahrens ace2.a_who = -1; 952789Sahrens return (memcmp(&ace1, &ace2, sizeof (ace_t))); 953789Sahrens } 954789Sahrens 955789Sahrens static int 956789Sahrens aclent_match(void *entry1, void *entry2) 957789Sahrens { 958789Sahrens aclent_t *aclent1 = (aclent_t *)entry1; 959789Sahrens aclent_t *aclent2 = (aclent_t *)entry2; 960789Sahrens 961789Sahrens return (memcmp(aclent1, aclent2, sizeof (aclent_t))); 962789Sahrens } 963789Sahrens 964789Sahrens /* 965789Sahrens * Find acl entries in acl that correspond to removeacl. Search 966789Sahrens * is started from slot. The flag argument indicates whether to 967789Sahrens * remove all matches or just the first match. 968789Sahrens */ 969789Sahrens int 970789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag) 971789Sahrens { 972789Sahrens int i, j; 973789Sahrens int match; 974789Sahrens int (*acl_match)(void *acl1, void *acl2); 975789Sahrens void *acl_entry, *remove_entry; 976789Sahrens void *start; 977789Sahrens int found = 0; 978789Sahrens 979789Sahrens if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST) 980789Sahrens flag = ACL_REMOVE_FIRST; 981789Sahrens 982789Sahrens if (acl == NULL || removeacl == NULL) 983789Sahrens return (EACL_NO_ACL_ENTRY); 984789Sahrens 985789Sahrens if (acl->acl_type != removeacl->acl_type) 986789Sahrens return (EACL_DIFF_TYPE); 987789Sahrens 988789Sahrens if (acl->acl_type == ACLENT_T) 989789Sahrens acl_match = aclent_match; 990789Sahrens else 991789Sahrens acl_match = ace_match; 992789Sahrens 993789Sahrens for (i = 0, remove_entry = removeacl->acl_aclp; 994789Sahrens i != removeacl->acl_cnt; i++) { 995789Sahrens 996789Sahrens j = 0; 997789Sahrens acl_entry = (char *)acl->acl_aclp + 998789Sahrens (acl->acl_entry_size * start_slot); 999789Sahrens for (;;) { 1000789Sahrens match = acl_match(acl_entry, remove_entry); 1001789Sahrens if (match == 0) { 1002789Sahrens found++; 1003789Sahrens start = (char *)acl_entry + 1004789Sahrens acl->acl_entry_size; 1005789Sahrens (void) memmove(acl_entry, start, 1006789Sahrens acl->acl_entry_size * 1007789Sahrens acl->acl_cnt-- - (j + 1)); 1008789Sahrens 1009789Sahrens if (flag == ACL_REMOVE_FIRST) 1010789Sahrens break; 1011789Sahrens /* 1012789Sahrens * List has changed, restart search from 1013789Sahrens * beginning. 1014789Sahrens */ 1015789Sahrens acl_entry = acl->acl_aclp; 1016789Sahrens j = 0; 1017789Sahrens continue; 1018789Sahrens } 1019789Sahrens acl_entry = ((char *)acl_entry + acl->acl_entry_size); 1020789Sahrens if (++j >= acl->acl_cnt) { 1021789Sahrens break; 1022789Sahrens } 1023789Sahrens } 1024789Sahrens } 1025789Sahrens 1026789Sahrens return ((found == 0) ? EACL_NO_ACL_ENTRY : 0); 1027789Sahrens } 1028789Sahrens 1029789Sahrens /* 1030789Sahrens * Replace entires entries in acl1 with the corresponding entries 1031789Sahrens * in newentries. The where argument specifies where to begin 1032789Sahrens * the replacement. If the where argument is 1 greater than the 1033789Sahrens * number of acl entries in acl1 then they are appended. If the 1034789Sahrens * where argument is 2+ greater than the number of acl entries then 1035789Sahrens * EACL_INVALID_SLOT is returned. 1036789Sahrens */ 1037789Sahrens int 1038789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where) 1039789Sahrens { 1040789Sahrens 1041789Sahrens int slot; 1042789Sahrens int slots_needed; 1043789Sahrens int slots_left; 1044789Sahrens int newsize; 1045789Sahrens 1046789Sahrens if (acl1 == NULL || newentries == NULL) 1047789Sahrens return (EACL_NO_ACL_ENTRY); 1048789Sahrens 1049789Sahrens if (where < 0 || where >= acl1->acl_cnt) 1050789Sahrens return (EACL_INVALID_SLOT); 1051789Sahrens 1052789Sahrens if (acl1->acl_type != newentries->acl_type) 1053789Sahrens return (EACL_DIFF_TYPE); 1054789Sahrens 1055789Sahrens slot = where; 1056789Sahrens 1057789Sahrens slots_left = acl1->acl_cnt - slot + 1; 1058789Sahrens if (slots_left < newentries->acl_cnt) { 1059789Sahrens slots_needed = newentries->acl_cnt - slots_left; 1060789Sahrens newsize = (acl1->acl_entry_size * acl1->acl_cnt) + 1061789Sahrens (acl1->acl_entry_size * slots_needed); 1062789Sahrens acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); 1063789Sahrens if (acl1->acl_aclp == NULL) 1064789Sahrens return (-1); 1065789Sahrens } 1066789Sahrens (void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot), 1067789Sahrens newentries->acl_aclp, 1068789Sahrens newentries->acl_entry_size * newentries->acl_cnt); 1069789Sahrens 1070789Sahrens /* 1071789Sahrens * Did ACL grow? 1072789Sahrens */ 1073789Sahrens 1074789Sahrens if ((slot + newentries->acl_cnt) > acl1->acl_cnt) { 1075789Sahrens acl1->acl_cnt = slot + newentries->acl_cnt; 1076789Sahrens } 1077789Sahrens 1078789Sahrens return (0); 1079789Sahrens } 1080789Sahrens 1081789Sahrens /* 1082789Sahrens * Add acl2 entries into acl1. The where argument specifies where 1083789Sahrens * to add the entries. 1084789Sahrens */ 1085789Sahrens int 1086789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where) 1087789Sahrens { 1088789Sahrens 1089789Sahrens int newsize; 1090789Sahrens int len; 1091789Sahrens void *start; 1092789Sahrens void *to; 1093789Sahrens 1094789Sahrens if (acl1 == NULL || acl2 == NULL) 1095789Sahrens return (EACL_NO_ACL_ENTRY); 1096789Sahrens 1097789Sahrens if (acl1->acl_type != acl2->acl_type) 1098789Sahrens return (EACL_DIFF_TYPE); 1099789Sahrens 1100789Sahrens /* 1101789Sahrens * allow where to specify 1 past last slot for an append operation 1102789Sahrens * but anything greater is an error. 1103789Sahrens */ 1104789Sahrens if (where < 0 || where > acl1->acl_cnt) 1105789Sahrens return (EACL_INVALID_SLOT); 1106789Sahrens 1107789Sahrens newsize = (acl2->acl_entry_size * acl2->acl_cnt) + 1108789Sahrens (acl1->acl_entry_size * acl1->acl_cnt); 1109789Sahrens acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); 1110789Sahrens if (acl1->acl_aclp == NULL) 1111789Sahrens return (-1); 1112789Sahrens 1113789Sahrens /* 1114789Sahrens * first push down entries where new ones will be inserted 1115789Sahrens */ 1116789Sahrens 1117789Sahrens to = (void *)((char *)acl1->acl_aclp + 1118789Sahrens ((where + acl2->acl_cnt) * acl1->acl_entry_size)); 1119789Sahrens 1120789Sahrens start = (void *)((char *)acl1->acl_aclp + 1121789Sahrens where * acl1->acl_entry_size); 1122789Sahrens 1123789Sahrens if (where < acl1->acl_cnt) { 1124789Sahrens len = (acl1->acl_cnt - where) * acl1->acl_entry_size; 1125789Sahrens (void) memmove(to, start, len); 1126789Sahrens } 1127789Sahrens 1128789Sahrens /* 1129789Sahrens * now stick in new entries. 1130789Sahrens */ 1131789Sahrens 1132789Sahrens (void) memmove(start, acl2->acl_aclp, 1133789Sahrens acl2->acl_cnt * acl2->acl_entry_size); 1134789Sahrens 1135789Sahrens acl1->acl_cnt += acl2->acl_cnt; 1136789Sahrens return (0); 1137789Sahrens } 1138789Sahrens 1139789Sahrens static void 1140789Sahrens aclent_perms(int perm, char *txt_perms) 1141789Sahrens { 1142789Sahrens if (perm & S_IROTH) 1143789Sahrens txt_perms[0] = 'r'; 1144789Sahrens else 1145789Sahrens txt_perms[0] = '-'; 1146789Sahrens if (perm & S_IWOTH) 1147789Sahrens txt_perms[1] = 'w'; 1148789Sahrens else 1149789Sahrens txt_perms[1] = '-'; 1150789Sahrens if (perm & S_IXOTH) 1151789Sahrens txt_perms[2] = 'x'; 1152789Sahrens else 1153789Sahrens txt_perms[2] = '-'; 1154789Sahrens txt_perms[3] = '\0'; 1155789Sahrens } 1156789Sahrens 1157789Sahrens static char * 1158789Sahrens pruname(uid_t uid) 1159789Sahrens { 1160789Sahrens struct passwd *passwdp; 1161789Sahrens static char uidp[10]; /* big enough */ 1162789Sahrens 1163789Sahrens passwdp = getpwuid(uid); 1164789Sahrens if (passwdp == (struct passwd *)NULL) { 1165789Sahrens /* could not get passwd information: display uid instead */ 1166789Sahrens (void) sprintf(uidp, "%ld", (long)uid); 1167789Sahrens return (uidp); 1168789Sahrens } else 1169789Sahrens return (passwdp->pw_name); 1170789Sahrens } 1171789Sahrens 1172789Sahrens static char * 1173789Sahrens prgname(gid_t gid) 1174789Sahrens { 1175789Sahrens struct group *groupp; 1176789Sahrens static char gidp[10]; /* big enough */ 1177789Sahrens 1178789Sahrens groupp = getgrgid(gid); 1179789Sahrens if (groupp == (struct group *)NULL) { 1180789Sahrens /* could not get group information: display gid instead */ 1181789Sahrens (void) sprintf(gidp, "%ld", (long)gid); 1182789Sahrens return (gidp); 1183789Sahrens } else 1184789Sahrens return (groupp->gr_name); 1185789Sahrens } 1186789Sahrens static void 1187789Sahrens aclent_printacl(acl_t *aclp) 1188789Sahrens { 1189789Sahrens aclent_t *tp; 1190789Sahrens int aclcnt; 1191789Sahrens int mask; 1192789Sahrens int slot = 0; 1193789Sahrens char perm[4]; 1194789Sahrens 1195789Sahrens /* display ACL: assume it is sorted. */ 1196789Sahrens aclcnt = aclp->acl_cnt; 1197789Sahrens for (tp = aclp->acl_aclp; aclcnt--; tp++) { 1198789Sahrens if (tp->a_type == CLASS_OBJ) 1199789Sahrens mask = tp->a_perm; 1200789Sahrens } 1201789Sahrens aclcnt = aclp->acl_cnt; 1202789Sahrens for (tp = aclp->acl_aclp; aclcnt--; tp++) { 1203789Sahrens (void) printf(" %d:", slot++); 1204789Sahrens switch (tp->a_type) { 1205789Sahrens case USER: 1206789Sahrens aclent_perms(tp->a_perm, perm); 1207789Sahrens (void) printf("user:%s:%s\t\t", 1208789Sahrens pruname(tp->a_id), perm); 1209789Sahrens aclent_perms((tp->a_perm & mask), perm); 1210789Sahrens (void) printf("#effective:%s\n", perm); 1211789Sahrens break; 1212789Sahrens case USER_OBJ: 1213789Sahrens /* no need to display uid */ 1214789Sahrens aclent_perms(tp->a_perm, perm); 1215789Sahrens (void) printf("user::%s\n", perm); 1216789Sahrens break; 1217789Sahrens case GROUP: 1218789Sahrens aclent_perms(tp->a_perm, perm); 1219789Sahrens (void) printf("group:%s:%s\t\t", 1220789Sahrens prgname(tp->a_id), perm); 1221789Sahrens aclent_perms(tp->a_perm & mask, perm); 1222789Sahrens (void) printf("#effective:%s\n", perm); 1223789Sahrens break; 1224789Sahrens case GROUP_OBJ: 1225789Sahrens aclent_perms(tp->a_perm, perm); 1226789Sahrens (void) printf("group::%s\t\t", perm); 1227789Sahrens aclent_perms(tp->a_perm & mask, perm); 1228789Sahrens (void) printf("#effective:%s\n", perm); 1229789Sahrens break; 1230789Sahrens case CLASS_OBJ: 1231789Sahrens aclent_perms(tp->a_perm, perm); 1232789Sahrens (void) printf("mask:%s\n", perm); 1233789Sahrens break; 1234789Sahrens case OTHER_OBJ: 1235789Sahrens aclent_perms(tp->a_perm, perm); 1236789Sahrens (void) printf("other:%s\n", perm); 1237789Sahrens break; 1238789Sahrens case DEF_USER: 1239789Sahrens aclent_perms(tp->a_perm, perm); 1240789Sahrens (void) printf("default:user:%s:%s\n", 1241789Sahrens pruname(tp->a_id), perm); 1242789Sahrens break; 1243789Sahrens case DEF_USER_OBJ: 1244789Sahrens aclent_perms(tp->a_perm, perm); 1245789Sahrens (void) printf("default:user::%s\n", perm); 1246789Sahrens break; 1247789Sahrens case DEF_GROUP: 1248789Sahrens aclent_perms(tp->a_perm, perm); 1249789Sahrens (void) printf("default:group:%s:%s\n", 1250789Sahrens prgname(tp->a_id), perm); 1251789Sahrens break; 1252789Sahrens case DEF_GROUP_OBJ: 1253789Sahrens aclent_perms(tp->a_perm, perm); 1254789Sahrens (void) printf("default:group::%s\n", perm); 1255789Sahrens break; 1256789Sahrens case DEF_CLASS_OBJ: 1257789Sahrens aclent_perms(tp->a_perm, perm); 1258789Sahrens (void) printf("default:mask:%s\n", perm); 1259789Sahrens break; 1260789Sahrens case DEF_OTHER_OBJ: 1261789Sahrens aclent_perms(tp->a_perm, perm); 1262789Sahrens (void) printf("default:other:%s\n", perm); 1263789Sahrens break; 1264789Sahrens default: 1265789Sahrens (void) fprintf(stderr, 1266789Sahrens gettext("unrecognized entry\n")); 1267789Sahrens break; 1268789Sahrens } 1269789Sahrens } 1270789Sahrens } 1271789Sahrens 1272789Sahrens static void 1273789Sahrens split_line(char *str, int cols) 1274789Sahrens { 1275789Sahrens char *ptr; 1276789Sahrens int len; 1277789Sahrens int i; 1278789Sahrens int last_split; 1279789Sahrens char pad[11]; 1280789Sahrens int pad_len; 1281789Sahrens 1282789Sahrens len = strlen(str); 1283789Sahrens ptr = str; 1284789Sahrens (void) strcpy(pad, ""); 1285789Sahrens pad_len = 0; 1286789Sahrens 1287789Sahrens ptr = str; 1288789Sahrens last_split = 0; 1289789Sahrens for (i = 0; i != len; i++) { 1290789Sahrens if ((i + pad_len + 4) >= cols) { 1291789Sahrens (void) printf("%s%.*s\n", pad, last_split, ptr); 1292789Sahrens ptr = &ptr[last_split]; 1293789Sahrens len = strlen(ptr); 1294789Sahrens i = 0; 1295789Sahrens pad_len = 4; 1296789Sahrens (void) strcpy(pad, " "); 1297789Sahrens } else { 1298789Sahrens if (ptr[i] == '/' || ptr[i] == ':') { 1299789Sahrens last_split = i; 1300789Sahrens } 1301789Sahrens } 1302789Sahrens } 1303789Sahrens if (i == len) { 1304789Sahrens (void) printf("%s%s\n", pad, ptr); 1305789Sahrens } 1306789Sahrens } 1307789Sahrens 1308789Sahrens static void 1309789Sahrens ace_printacl(acl_t *aclp, int cols) 1310789Sahrens { 1311789Sahrens int slot = 0; 1312789Sahrens char *token; 1313789Sahrens char *acltext; 1314789Sahrens 1315789Sahrens acltext = acl_totext(aclp); 1316789Sahrens 1317789Sahrens if (acltext == NULL) 1318789Sahrens return; 1319789Sahrens 1320789Sahrens token = strtok(acltext, ","); 1321789Sahrens if (token == NULL) { 1322789Sahrens free(acltext); 1323789Sahrens return; 1324789Sahrens } 1325789Sahrens 1326789Sahrens do { 1327789Sahrens (void) printf(" %d:", slot++); 1328789Sahrens split_line(token, cols - 5); 1329789Sahrens } while (token = strtok(NULL, ",")); 1330789Sahrens free(acltext); 1331789Sahrens } 1332789Sahrens 1333789Sahrens /* 1334789Sahrens * pretty print an ACL. 1335789Sahrens * For aclent_t ACL's the format is 1336789Sahrens * similar to the old format used by getfacl, 1337789Sahrens * with the addition of adding a "slot" number 1338789Sahrens * before each entry. 1339789Sahrens * 1340789Sahrens * for ace_t ACL's the cols variable will break up 1341789Sahrens * the long lines into multiple lines and will also 1342789Sahrens * print a "slot" number. 1343789Sahrens */ 1344789Sahrens void 1345789Sahrens acl_printacl(acl_t *aclp, int cols) 1346789Sahrens { 1347789Sahrens 1348789Sahrens switch (aclp->acl_type) { 1349789Sahrens case ACLENT_T: 1350789Sahrens aclent_printacl(aclp); 1351789Sahrens break; 1352789Sahrens case ACE_T: 1353789Sahrens ace_printacl(aclp, cols); 1354789Sahrens break; 1355789Sahrens } 1356789Sahrens } 1357789Sahrens 1358789Sahrens 1359789Sahrens /* 1360789Sahrens * return text for an ACL error. 1361789Sahrens */ 1362789Sahrens char * 1363789Sahrens acl_strerror(int errnum) 1364789Sahrens { 1365789Sahrens switch (errnum) { 1366789Sahrens case EACL_GRP_ERROR: 1367789Sahrens return (dgettext(TEXT_DOMAIN, 1368789Sahrens "There is more than one user group owner entry")); 1369789Sahrens case EACL_USER_ERROR: 1370789Sahrens return (dgettext(TEXT_DOMAIN, 1371789Sahrens "There is more than one user owner entry")); 1372789Sahrens case EACL_OTHER_ERROR: 1373789Sahrens return (dgettext(TEXT_DOMAIN, 1374789Sahrens "There is more than one other entry")); 1375789Sahrens case EACL_CLASS_ERROR: 1376789Sahrens return (dgettext(TEXT_DOMAIN, 1377789Sahrens "There is more than one mask entry")); 1378789Sahrens case EACL_DUPLICATE_ERROR: 1379789Sahrens return (dgettext(TEXT_DOMAIN, 1380789Sahrens "Duplicate user or group entries")); 1381789Sahrens case EACL_MISS_ERROR: 1382789Sahrens return (dgettext(TEXT_DOMAIN, 1383789Sahrens "Missing user/group owner, other, mask entry")); 1384789Sahrens case EACL_MEM_ERROR: 1385789Sahrens return (dgettext(TEXT_DOMAIN, 1386789Sahrens "Memory error")); 1387789Sahrens case EACL_ENTRY_ERROR: 1388789Sahrens return (dgettext(TEXT_DOMAIN, 1389789Sahrens "Unrecognized entry type")); 1390789Sahrens case EACL_INHERIT_ERROR: 1391789Sahrens return (dgettext(TEXT_DOMAIN, 1392789Sahrens "Invalid inheritance flags")); 1393789Sahrens case EACL_FLAGS_ERROR: 1394789Sahrens return (dgettext(TEXT_DOMAIN, 1395789Sahrens "Unrecognized entry flags")); 1396789Sahrens case EACL_PERM_MASK_ERROR: 1397789Sahrens return (dgettext(TEXT_DOMAIN, 1398789Sahrens "Invalid ACL permissions")); 1399789Sahrens case EACL_COUNT_ERROR: 1400789Sahrens return (dgettext(TEXT_DOMAIN, 1401789Sahrens "Invalid ACL count")); 1402789Sahrens case EACL_INVALID_SLOT: 1403789Sahrens return (dgettext(TEXT_DOMAIN, 1404789Sahrens "Invalid ACL entry number specified")); 1405789Sahrens case EACL_NO_ACL_ENTRY: 1406789Sahrens return (dgettext(TEXT_DOMAIN, 1407789Sahrens "ACL entry doesn't exist")); 1408789Sahrens case EACL_DIFF_TYPE: 1409789Sahrens return (dgettext(TEXT_DOMAIN, 1410789Sahrens "ACL type's are different")); 1411789Sahrens case EACL_INVALID_USER_GROUP: 1412789Sahrens return (dgettext(TEXT_DOMAIN, "Invalid user or group")); 1413789Sahrens case EACL_INVALID_STR: 1414789Sahrens return (dgettext(TEXT_DOMAIN, "ACL string is invalid")); 1415789Sahrens case EACL_FIELD_NOT_BLANK: 1416789Sahrens return (dgettext(TEXT_DOMAIN, "Field expected to be blank")); 1417789Sahrens case EACL_INVALID_ACCESS_TYPE: 1418789Sahrens return (dgettext(TEXT_DOMAIN, "Invalid access type")); 1419789Sahrens case EACL_UNKNOWN_DATA: 1420789Sahrens return (dgettext(TEXT_DOMAIN, "Unrecognized entry")); 1421789Sahrens case EACL_MISSING_FIELDS: 1422789Sahrens return (dgettext(TEXT_DOMAIN, 1423789Sahrens "ACL specification missing required fields")); 1424789Sahrens case EACL_INHERIT_NOTDIR: 1425789Sahrens return (dgettext(TEXT_DOMAIN, 1426789Sahrens "Inheritance flags are only allowed on directories")); 1427789Sahrens case -1: 1428789Sahrens return (strerror(errno)); 1429789Sahrens default: 1430789Sahrens errno = EINVAL; 1431789Sahrens return (dgettext(TEXT_DOMAIN, "Unknown error")); 1432789Sahrens } 1433789Sahrens } 1434