merge.c revision 204819
133965Sjdp/*- 2130561Sobrien * Copyright (c) 2001 Chris D. Faulhaber 333965Sjdp * All rights reserved. 433965Sjdp * 533965Sjdp * Redistribution and use in source and binary forms, with or without 633965Sjdp * modification, are permitted provided that the following conditions 733965Sjdp * are met: 833965Sjdp * 1. Redistributions of source code must retain the above copyright 933965Sjdp * notice, this list of conditions and the following disclaimer. 1033965Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1133965Sjdp * notice, this list of conditions and the following disclaimer in the 1233965Sjdp * documentation and/or other materials provided with the distribution. 1333965Sjdp * 1433965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1533965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1633965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1733965Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1833965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1933965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2133965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2233965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2333965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2433965Sjdp * SUCH DAMAGE. 2533965Sjdp */ 2633965Sjdp 2733965Sjdp#include <sys/cdefs.h> 2833965Sjdp__FBSDID("$FreeBSD: head/bin/setfacl/merge.c 204819 2010-03-07 07:59:05Z joel $"); 2933965Sjdp 3033965Sjdp#include <sys/types.h> 3133965Sjdp#include <sys/acl.h> 3233965Sjdp#include <sys/stat.h> 3333965Sjdp 3433965Sjdp#include <err.h> 3533965Sjdp#include <stdio.h> 3633965Sjdp 3733965Sjdp#include "setfacl.h" 3833965Sjdp 3933965Sjdpstatic int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, 4033965Sjdp int acl_brand); 4133965Sjdp 4233965Sjdpstatic int 4333965Sjdpmerge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) 4433965Sjdp{ 4533965Sjdp acl_permset_t permset; 4633965Sjdp acl_entry_type_t entry_type; 4733965Sjdp acl_flagset_t flagset; 4833965Sjdp int have_entry; 4933965Sjdp uid_t *id, *id_new; 5033965Sjdp 5133965Sjdp have_entry = 0; 5233965Sjdp 5333965Sjdp id = acl_get_qualifier(*entry); 5433965Sjdp if (id == NULL) 5533965Sjdp err(1, "acl_get_qualifier() failed"); 5633965Sjdp id_new = acl_get_qualifier(*entry_new); 5760484Sobrien if (id_new == NULL) 5833965Sjdp err(1, "acl_get_qualifier() failed"); 5933965Sjdp if (*id == *id_new) { 6033965Sjdp /* any other matches */ 6133965Sjdp if (acl_get_permset(*entry, &permset) == -1) 6233965Sjdp err(1, "acl_get_permset() failed"); 6333965Sjdp if (acl_set_permset(*entry_new, permset) == -1) 6433965Sjdp err(1, "acl_set_permset() failed"); 6560484Sobrien 6633965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 6733965Sjdp if (acl_get_entry_type_np(*entry, &entry_type)) 6833965Sjdp err(1, "acl_get_entry_type_np() failed"); 6933965Sjdp if (acl_set_entry_type_np(*entry_new, entry_type)) 7060484Sobrien err(1, "acl_set_entry_type_np() failed"); 7133965Sjdp if (acl_get_flagset_np(*entry, &flagset)) 7233965Sjdp err(1, "acl_get_flagset_np() failed"); 7333965Sjdp if (acl_set_flagset_np(*entry_new, flagset)) 7433965Sjdp err(1, "acl_set_flagset_np() failed"); 7533965Sjdp } 7633965Sjdp 7733965Sjdp have_entry = 1; 7833965Sjdp } 7933965Sjdp acl_free(id); 8033965Sjdp acl_free(id_new); 8133965Sjdp 8233965Sjdp return (have_entry); 8333965Sjdp} 8489857Sobrien 8533965Sjdp/* 8633965Sjdp * merge an ACL into existing file's ACL 8789857Sobrien */ 8833965Sjdpint 8933965Sjdpmerge_acl(acl_t acl, acl_t *prev_acl, const char *filename) 9089857Sobrien{ 9133965Sjdp acl_entry_t entry, entry_new; 9233965Sjdp acl_permset_t permset; 9389857Sobrien acl_t acl_new; 9433965Sjdp acl_tag_t tag, tag_new; 9533965Sjdp acl_entry_type_t entry_type, entry_type_new; 9633965Sjdp acl_flagset_t flagset; 9733965Sjdp int entry_id, entry_id_new, have_entry, entry_number = 0; 9833965Sjdp int acl_brand, prev_acl_brand; 9933965Sjdp 10033965Sjdp acl_get_brand_np(acl, &acl_brand); 10133965Sjdp acl_get_brand_np(*prev_acl, &prev_acl_brand); 10233965Sjdp 10333965Sjdp if (branding_mismatch(acl_brand, prev_acl_brand)) { 10433965Sjdp warnx("%s: branding mismatch; existing ACL is %s, " 10533965Sjdp "entry to be merged is %s", filename, 10633965Sjdp brand_name(prev_acl_brand), brand_name(acl_brand)); 10733965Sjdp return (-1); 10833965Sjdp } 10933965Sjdp 11033965Sjdp acl_new = acl_dup(*prev_acl); 111130561Sobrien if (acl_new == NULL) 11233965Sjdp err(1, "%s: acl_dup() failed", filename); 11333965Sjdp 11433965Sjdp entry_id = ACL_FIRST_ENTRY; 11533965Sjdp 11633965Sjdp while (acl_get_entry(acl, entry_id, &entry) == 1) { 11733965Sjdp entry_id = ACL_NEXT_ENTRY; 11833965Sjdp have_entry = 0; 11933965Sjdp 12033965Sjdp /* keep track of existing ACL_MASK entries */ 12133965Sjdp if (acl_get_tag_type(entry, &tag) == -1) 12233965Sjdp err(1, "%s: acl_get_tag_type() failed - " 12333965Sjdp "invalid ACL entry", filename); 12433965Sjdp if (tag == ACL_MASK) 12533965Sjdp have_mask = 1; 12633965Sjdp 12733965Sjdp /* check against the existing ACL entries */ 12833965Sjdp entry_id_new = ACL_FIRST_ENTRY; 12933965Sjdp while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { 130130561Sobrien entry_id_new = ACL_NEXT_ENTRY; 13133965Sjdp 13233965Sjdp if (acl_get_tag_type(entry, &tag) == -1) 13333965Sjdp err(1, "%s: acl_get_tag_type() failed", 13433965Sjdp filename); 13533965Sjdp if (acl_get_tag_type(entry_new, &tag_new) == -1) 13633965Sjdp err(1, "%s: acl_get_tag_type() failed", 13760484Sobrien filename); 13833965Sjdp if (tag != tag_new) 13933965Sjdp continue; 14033965Sjdp 14133965Sjdp /* 14233965Sjdp * For NFSv4, in addition to "tag" and "id" we also 14333965Sjdp * compare "entry_type". 14433965Sjdp */ 14533965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 14633965Sjdp if (acl_get_entry_type_np(entry, &entry_type)) 14733965Sjdp err(1, "%s: acl_get_entry_type_np() " 14833965Sjdp "failed", filename); 14933965Sjdp if (acl_get_entry_type_np(entry_new, &entry_type_new)) 15033965Sjdp err(1, "%s: acl_get_entry_type_np() " 15133965Sjdp "failed", filename); 15233965Sjdp if (entry_type != entry_type_new) 15333965Sjdp continue; 15433965Sjdp } 15533965Sjdp 15633965Sjdp switch(tag) { 15733965Sjdp case ACL_USER: 15833965Sjdp case ACL_GROUP: 15933965Sjdp have_entry = merge_user_group(&entry, 16033965Sjdp &entry_new, acl_brand); 16133965Sjdp if (have_entry == 0) 16233965Sjdp break; 16333965Sjdp /* FALLTHROUGH */ 16433965Sjdp case ACL_USER_OBJ: 16533965Sjdp case ACL_GROUP_OBJ: 16633965Sjdp case ACL_OTHER: 16733965Sjdp case ACL_MASK: 16833965Sjdp case ACL_EVERYONE: 16933965Sjdp if (acl_get_permset(entry, &permset) == -1) 17033965Sjdp err(1, "%s: acl_get_permset() failed", 17133965Sjdp filename); 17233965Sjdp if (acl_set_permset(entry_new, permset) == -1) 17333965Sjdp err(1, "%s: acl_set_permset() failed", 17433965Sjdp filename); 17533965Sjdp 17633965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 17733965Sjdp if (acl_get_entry_type_np(entry, &entry_type)) 17833965Sjdp err(1, "%s: acl_get_entry_type_np() failed", 17933965Sjdp filename); 18033965Sjdp if (acl_set_entry_type_np(entry_new, entry_type)) 18133965Sjdp err(1, "%s: acl_set_entry_type_np() failed", 18233965Sjdp filename); 18333965Sjdp if (acl_get_flagset_np(entry, &flagset)) 18433965Sjdp err(1, "%s: acl_get_flagset_np() failed", 18533965Sjdp filename); 18633965Sjdp if (acl_set_flagset_np(entry_new, flagset)) 18733965Sjdp err(1, "%s: acl_set_flagset_np() failed", 18833965Sjdp filename); 18933965Sjdp } 19033965Sjdp have_entry = 1; 19133965Sjdp break; 19233965Sjdp default: 19333965Sjdp /* should never be here */ 19433965Sjdp errx(1, "%s: invalid tag type: %i", filename, tag); 19533965Sjdp break; 19633965Sjdp } 19733965Sjdp } 19833965Sjdp 19933965Sjdp /* if this entry has not been found, it must be new */ 20033965Sjdp if (have_entry == 0) { 20133965Sjdp 20233965Sjdp /* 20333965Sjdp * NFSv4 ACL entries must be prepended to the ACL. 20433965Sjdp * Appending them at the end makes no sense, since 20533965Sjdp * in most cases they wouldn't even get evaluated. 20633965Sjdp */ 20733965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 20833965Sjdp if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 20933965Sjdp warn("%s: acl_create_entry_np() failed", filename); 21033965Sjdp acl_free(acl_new); 21133965Sjdp return (-1); 21233965Sjdp } 21333965Sjdp /* 21433965Sjdp * Without this increment, adding several 21533965Sjdp * entries at once, for example 21633965Sjdp * "setfacl -m user:1:r:allow,user:2:r:allow", 21733965Sjdp * would make them appear in reverse order. 21833965Sjdp */ 21933965Sjdp entry_number++; 22033965Sjdp } else { 22133965Sjdp if (acl_create_entry(&acl_new, &entry_new) == -1) { 22233965Sjdp warn("%s: acl_create_entry() failed", filename); 22333965Sjdp acl_free(acl_new); 22433965Sjdp return (-1); 22533965Sjdp } 22633965Sjdp } 22733965Sjdp if (acl_copy_entry(entry_new, entry) == -1) 22833965Sjdp err(1, "%s: acl_copy_entry() failed", filename); 22933965Sjdp } 23033965Sjdp } 23133965Sjdp 23233965Sjdp acl_free(*prev_acl); 23333965Sjdp *prev_acl = acl_new; 23433965Sjdp 23533965Sjdp return (0); 23633965Sjdp} 23733965Sjdp 238int 239add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) 240{ 241 acl_entry_t entry, entry_new; 242 acl_t acl_new; 243 int entry_id, acl_brand, prev_acl_brand; 244 245 acl_get_brand_np(acl, &acl_brand); 246 acl_get_brand_np(*prev_acl, &prev_acl_brand); 247 248 if (prev_acl_brand != ACL_BRAND_NFS4) { 249 warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", 250 filename); 251 return (-1); 252 } 253 254 if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) { 255 warnx("%s: branding mismatch; existing ACL is NFSv4, " 256 "entry to be added is %s", filename, 257 brand_name(acl_brand)); 258 return (-1); 259 } 260 261 acl_new = acl_dup(*prev_acl); 262 if (acl_new == NULL) 263 err(1, "%s: acl_dup() failed", filename); 264 265 entry_id = ACL_FIRST_ENTRY; 266 267 while (acl_get_entry(acl, entry_id, &entry) == 1) { 268 entry_id = ACL_NEXT_ENTRY; 269 270 if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 271 warn("%s: acl_create_entry_np() failed", filename); 272 acl_free(acl_new); 273 return (-1); 274 } 275 276 /* 277 * Without this increment, adding several 278 * entries at once, for example 279 * "setfacl -m user:1:r:allow,user:2:r:allow", 280 * would make them appear in reverse order. 281 */ 282 entry_number++; 283 284 if (acl_copy_entry(entry_new, entry) == -1) 285 err(1, "%s: acl_copy_entry() failed", filename); 286 } 287 288 acl_free(*prev_acl); 289 *prev_acl = acl_new; 290 291 return (0); 292} 293