1139969Simp/*- 274465Srwatson * Copyright (c) 2001 Chris D. Faulhaber 374465Srwatson * All rights reserved. 474465Srwatson * 574465Srwatson * Redistribution and use in source and binary forms, with or without 674465Srwatson * modification, are permitted provided that the following conditions 774465Srwatson * are met: 874465Srwatson * 1. Redistributions of source code must retain the above copyright 974465Srwatson * notice, this list of conditions and the following disclaimer. 1074465Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1174465Srwatson * notice, this list of conditions and the following disclaimer in the 1274465Srwatson * documentation and/or other materials provided with the distribution. 1374465Srwatson * 1474465Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1574465Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1674465Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17204819Sjoel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18204819Sjoel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204819Sjoel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204819Sjoel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204819Sjoel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204819Sjoel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204819Sjoel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204819Sjoel * SUCH DAMAGE. 2574465Srwatson */ 2674465Srwatson 2799110Sobrien#include <sys/cdefs.h> 2899110Sobrien__FBSDID("$FreeBSD$"); 2999110Sobrien 3074465Srwatson#include <sys/types.h> 3174465Srwatson#include <sys/acl.h> 3274465Srwatson#include <sys/stat.h> 3374465Srwatson 3474465Srwatson#include <err.h> 3574465Srwatson#include <stdio.h> 3674465Srwatson 3774465Srwatson#include "setfacl.h" 3874465Srwatson 39196936Straszstatic int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, 40196936Strasz int acl_brand); 4187260Sjedgar 4287260Sjedgarstatic int 43196936Straszmerge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) 4487260Sjedgar{ 4587260Sjedgar acl_permset_t permset; 46196936Strasz acl_entry_type_t entry_type; 47196936Strasz acl_flagset_t flagset; 4887260Sjedgar int have_entry; 4987260Sjedgar uid_t *id, *id_new; 5087260Sjedgar 5187260Sjedgar have_entry = 0; 5287260Sjedgar 5387260Sjedgar id = acl_get_qualifier(*entry); 5487260Sjedgar if (id == NULL) 5587260Sjedgar err(1, "acl_get_qualifier() failed"); 5687260Sjedgar id_new = acl_get_qualifier(*entry_new); 5787260Sjedgar if (id_new == NULL) 5887260Sjedgar err(1, "acl_get_qualifier() failed"); 5987260Sjedgar if (*id == *id_new) { 6087260Sjedgar /* any other matches */ 6187260Sjedgar if (acl_get_permset(*entry, &permset) == -1) 6287260Sjedgar err(1, "acl_get_permset() failed"); 6387260Sjedgar if (acl_set_permset(*entry_new, permset) == -1) 6487260Sjedgar err(1, "acl_set_permset() failed"); 65196936Strasz 66196936Strasz if (acl_brand == ACL_BRAND_NFS4) { 67196936Strasz if (acl_get_entry_type_np(*entry, &entry_type)) 68196936Strasz err(1, "acl_get_entry_type_np() failed"); 69196936Strasz if (acl_set_entry_type_np(*entry_new, entry_type)) 70196936Strasz err(1, "acl_set_entry_type_np() failed"); 71196936Strasz if (acl_get_flagset_np(*entry, &flagset)) 72196936Strasz err(1, "acl_get_flagset_np() failed"); 73196936Strasz if (acl_set_flagset_np(*entry_new, flagset)) 74196936Strasz err(1, "acl_set_flagset_np() failed"); 75196936Strasz } 76196936Strasz 7787260Sjedgar have_entry = 1; 7887260Sjedgar } 7987260Sjedgar acl_free(id); 8087260Sjedgar acl_free(id_new); 8187260Sjedgar 8287260Sjedgar return (have_entry); 8387260Sjedgar} 8487260Sjedgar 8587254Sjedgar/* 8687254Sjedgar * merge an ACL into existing file's ACL 8787254Sjedgar */ 8874465Srwatsonint 89196936Straszmerge_acl(acl_t acl, acl_t *prev_acl, const char *filename) 9074465Srwatson{ 9175928Sjedgar acl_entry_t entry, entry_new; 9275928Sjedgar acl_permset_t permset; 9374465Srwatson acl_t acl_new; 9475928Sjedgar acl_tag_t tag, tag_new; 95196936Strasz acl_entry_type_t entry_type, entry_type_new; 96196936Strasz acl_flagset_t flagset; 97240087Strasz int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0; 98196936Strasz int acl_brand, prev_acl_brand; 9974465Srwatson 100196936Strasz acl_get_brand_np(acl, &acl_brand); 101196936Strasz acl_get_brand_np(*prev_acl, &prev_acl_brand); 102196936Strasz 103201016Strasz if (branding_mismatch(acl_brand, prev_acl_brand)) { 104196936Strasz warnx("%s: branding mismatch; existing ACL is %s, " 105196936Strasz "entry to be merged is %s", filename, 106201016Strasz brand_name(prev_acl_brand), brand_name(acl_brand)); 107196936Strasz return (-1); 108196936Strasz } 109196936Strasz 110196936Strasz acl_new = acl_dup(*prev_acl); 11187254Sjedgar if (acl_new == NULL) 112196936Strasz err(1, "%s: acl_dup() failed", filename); 11374465Srwatson 11475928Sjedgar entry_id = ACL_FIRST_ENTRY; 11575928Sjedgar 11675928Sjedgar while (acl_get_entry(acl, entry_id, &entry) == 1) { 11775928Sjedgar entry_id = ACL_NEXT_ENTRY; 11874465Srwatson have_entry = 0; 119240087Strasz had_entry = 0; 12074465Srwatson 12175928Sjedgar /* keep track of existing ACL_MASK entries */ 12275928Sjedgar if (acl_get_tag_type(entry, &tag) == -1) 123196936Strasz err(1, "%s: acl_get_tag_type() failed - " 124196936Strasz "invalid ACL entry", filename); 12575928Sjedgar if (tag == ACL_MASK) 12674465Srwatson have_mask = 1; 12774465Srwatson 12874465Srwatson /* check against the existing ACL entries */ 12975928Sjedgar entry_id_new = ACL_FIRST_ENTRY; 130196936Strasz while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { 13175928Sjedgar entry_id_new = ACL_NEXT_ENTRY; 13275928Sjedgar 13375928Sjedgar if (acl_get_tag_type(entry, &tag) == -1) 134196936Strasz err(1, "%s: acl_get_tag_type() failed", 135196936Strasz filename); 13675928Sjedgar if (acl_get_tag_type(entry_new, &tag_new) == -1) 137196936Strasz err(1, "%s: acl_get_tag_type() failed", 138196936Strasz filename); 13975928Sjedgar if (tag != tag_new) 14075928Sjedgar continue; 14175928Sjedgar 142196936Strasz /* 143196936Strasz * For NFSv4, in addition to "tag" and "id" we also 144196936Strasz * compare "entry_type". 145196936Strasz */ 146196936Strasz if (acl_brand == ACL_BRAND_NFS4) { 147196936Strasz if (acl_get_entry_type_np(entry, &entry_type)) 148196936Strasz err(1, "%s: acl_get_entry_type_np() " 149196936Strasz "failed", filename); 150196936Strasz if (acl_get_entry_type_np(entry_new, &entry_type_new)) 151196936Strasz err(1, "%s: acl_get_entry_type_np() " 152196936Strasz "failed", filename); 153196936Strasz if (entry_type != entry_type_new) 154196936Strasz continue; 155196936Strasz } 156196936Strasz 15775928Sjedgar switch(tag) { 15875928Sjedgar case ACL_USER: 15975928Sjedgar case ACL_GROUP: 16087260Sjedgar have_entry = merge_user_group(&entry, 161196936Strasz &entry_new, acl_brand); 16287254Sjedgar if (have_entry == 0) 16374465Srwatson break; 16475928Sjedgar /* FALLTHROUGH */ 16575928Sjedgar case ACL_USER_OBJ: 16675928Sjedgar case ACL_GROUP_OBJ: 16775928Sjedgar case ACL_OTHER: 16875928Sjedgar case ACL_MASK: 169196936Strasz case ACL_EVERYONE: 17075928Sjedgar if (acl_get_permset(entry, &permset) == -1) 171196936Strasz err(1, "%s: acl_get_permset() failed", 172196936Strasz filename); 17375928Sjedgar if (acl_set_permset(entry_new, permset) == -1) 174196936Strasz err(1, "%s: acl_set_permset() failed", 175196936Strasz filename); 176196936Strasz 177196936Strasz if (acl_brand == ACL_BRAND_NFS4) { 178196936Strasz if (acl_get_entry_type_np(entry, &entry_type)) 179196936Strasz err(1, "%s: acl_get_entry_type_np() failed", 180196936Strasz filename); 181196936Strasz if (acl_set_entry_type_np(entry_new, entry_type)) 182196936Strasz err(1, "%s: acl_set_entry_type_np() failed", 183196936Strasz filename); 184196936Strasz if (acl_get_flagset_np(entry, &flagset)) 185196936Strasz err(1, "%s: acl_get_flagset_np() failed", 186196936Strasz filename); 187196936Strasz if (acl_set_flagset_np(entry_new, flagset)) 188196936Strasz err(1, "%s: acl_set_flagset_np() failed", 189196936Strasz filename); 190196936Strasz } 191240087Strasz had_entry = have_entry = 1; 19275928Sjedgar break; 19375928Sjedgar default: 19475928Sjedgar /* should never be here */ 195196936Strasz errx(1, "%s: invalid tag type: %i", filename, tag); 19675928Sjedgar break; 19774465Srwatson } 19874465Srwatson } 19974465Srwatson 20074465Srwatson /* if this entry has not been found, it must be new */ 201240087Strasz if (had_entry == 0) { 202196936Strasz 203196936Strasz /* 204196936Strasz * NFSv4 ACL entries must be prepended to the ACL. 205196936Strasz * Appending them at the end makes no sense, since 206196936Strasz * in most cases they wouldn't even get evaluated. 207196936Strasz */ 208196936Strasz if (acl_brand == ACL_BRAND_NFS4) { 209196936Strasz if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 210196936Strasz warn("%s: acl_create_entry_np() failed", filename); 211196936Strasz acl_free(acl_new); 212196936Strasz return (-1); 213196936Strasz } 214196936Strasz /* 215196936Strasz * Without this increment, adding several 216196936Strasz * entries at once, for example 217196936Strasz * "setfacl -m user:1:r:allow,user:2:r:allow", 218196936Strasz * would make them appear in reverse order. 219196936Strasz */ 220196936Strasz entry_number++; 221196936Strasz } else { 222196936Strasz if (acl_create_entry(&acl_new, &entry_new) == -1) { 223196936Strasz warn("%s: acl_create_entry() failed", filename); 224196936Strasz acl_free(acl_new); 225196936Strasz return (-1); 226196936Strasz } 22774465Srwatson } 22875928Sjedgar if (acl_copy_entry(entry_new, entry) == -1) 229196936Strasz err(1, "%s: acl_copy_entry() failed", filename); 23074465Srwatson } 23174465Srwatson } 23274465Srwatson 233196936Strasz acl_free(*prev_acl); 234196936Strasz *prev_acl = acl_new; 235196936Strasz 236196936Strasz return (0); 237196936Strasz} 238196936Strasz 239196936Straszint 240196936Straszadd_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) 241196936Strasz{ 242196936Strasz acl_entry_t entry, entry_new; 243196936Strasz acl_t acl_new; 244196936Strasz int entry_id, acl_brand, prev_acl_brand; 245196936Strasz 246196936Strasz acl_get_brand_np(acl, &acl_brand); 247196936Strasz acl_get_brand_np(*prev_acl, &prev_acl_brand); 248196936Strasz 249196936Strasz if (prev_acl_brand != ACL_BRAND_NFS4) { 250196936Strasz warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", 251196936Strasz filename); 252196936Strasz return (-1); 25375928Sjedgar } 25475928Sjedgar 255201016Strasz if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) { 256196936Strasz warnx("%s: branding mismatch; existing ACL is NFSv4, " 257201016Strasz "entry to be added is %s", filename, 258201016Strasz brand_name(acl_brand)); 259196936Strasz return (-1); 260196936Strasz } 261196936Strasz 262196936Strasz acl_new = acl_dup(*prev_acl); 263196936Strasz if (acl_new == NULL) 264196936Strasz err(1, "%s: acl_dup() failed", filename); 265196936Strasz 266196936Strasz entry_id = ACL_FIRST_ENTRY; 267196936Strasz 268196936Strasz while (acl_get_entry(acl, entry_id, &entry) == 1) { 269196936Strasz entry_id = ACL_NEXT_ENTRY; 270196936Strasz 271196936Strasz if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 272196936Strasz warn("%s: acl_create_entry_np() failed", filename); 273196936Strasz acl_free(acl_new); 274196936Strasz return (-1); 275196936Strasz } 276196936Strasz 277196936Strasz /* 278196936Strasz * Without this increment, adding several 279196936Strasz * entries at once, for example 280196936Strasz * "setfacl -m user:1:r:allow,user:2:r:allow", 281196936Strasz * would make them appear in reverse order. 282196936Strasz */ 283196936Strasz entry_number++; 284196936Strasz 285196936Strasz if (acl_copy_entry(entry_new, entry) == -1) 286196936Strasz err(1, "%s: acl_copy_entry() failed", filename); 287196936Strasz } 288196936Strasz 289196936Strasz acl_free(*prev_acl); 290196936Strasz *prev_acl = acl_new; 291196936Strasz 29287254Sjedgar return (0); 29374465Srwatson} 294