merge.c revision 256281
133965Sjdp/*- 233965Sjdp * Copyright (c) 2001 Chris D. Faulhaber 333965Sjdp * All rights reserved. 433965Sjdp * 560484Sobrien * Redistribution and use in source and binary forms, with or without 660484Sobrien * modification, are permitted provided that the following conditions 760484Sobrien * are met: 860484Sobrien * 1. Redistributions of source code must retain the above copyright 960484Sobrien * notice, this list of conditions and the following disclaimer. 1060484Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1160484Sobrien * notice, this list of conditions and the following disclaimer in the 1260484Sobrien * documentation and/or other materials provided with the distribution. 1360484Sobrien * 1460484Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1560484Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1660484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1760484Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1860484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1960484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2060484Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2160484Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2260484Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2360484Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2460484Sobrien * SUCH DAMAGE. 2560484Sobrien */ 2660484Sobrien 2760484Sobrien#include <sys/cdefs.h> 2860484Sobrien__FBSDID("$FreeBSD: stable/10/bin/setfacl/merge.c 240087 2012-09-04 12:19:34Z trasz $"); 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 3789857Sobrien#include "setfacl.h" 3889857Sobrien 3989857Sobrienstatic int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, 4089857Sobrien int acl_brand); 4189857Sobrien 4289857Sobrienstatic int 4389857Sobrienmerge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) 4489857Sobrien{ 4589857Sobrien acl_permset_t permset; 4689857Sobrien acl_entry_type_t entry_type; 4789857Sobrien acl_flagset_t flagset; 4889857Sobrien int have_entry; 4989857Sobrien uid_t *id, *id_new; 5089857Sobrien 5189857Sobrien have_entry = 0; 5289857Sobrien 5389857Sobrien id = acl_get_qualifier(*entry); 5489857Sobrien if (id == NULL) 5533965Sjdp err(1, "acl_get_qualifier() failed"); 5633965Sjdp id_new = acl_get_qualifier(*entry_new); 5733965Sjdp 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"); 6533965Sjdp 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)) 7033965Sjdp err(1, "acl_set_entry_type_np() failed"); 7160484Sobrien if (acl_get_flagset_np(*entry, &flagset)) 7260484Sobrien err(1, "acl_get_flagset_np() failed"); 7360484Sobrien if (acl_set_flagset_np(*entry_new, flagset)) 7433965Sjdp err(1, "acl_set_flagset_np() failed"); 7533965Sjdp } 7660484Sobrien 7760484Sobrien have_entry = 1; 7860484Sobrien } 7933965Sjdp acl_free(id); 8033965Sjdp acl_free(id_new); 8133965Sjdp 8233965Sjdp return (have_entry); 83218822Sdim} 8433965Sjdp 8533965Sjdp/* 8633965Sjdp * merge an ACL into existing file's ACL 8733965Sjdp */ 8833965Sjdpint 8933965Sjdpmerge_acl(acl_t acl, acl_t *prev_acl, const char *filename) 9033965Sjdp{ 9133965Sjdp acl_entry_t entry, entry_new; 9233965Sjdp acl_permset_t permset; 9333965Sjdp 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, had_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); 11133965Sjdp 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 had_entry = 0; 12033965Sjdp 12133965Sjdp /* keep track of existing ACL_MASK entries */ 12233965Sjdp if (acl_get_tag_type(entry, &tag) == -1) 12333965Sjdp err(1, "%s: acl_get_tag_type() failed - " 12433965Sjdp "invalid ACL entry", filename); 12533965Sjdp if (tag == ACL_MASK) 12633965Sjdp have_mask = 1; 12733965Sjdp 12833965Sjdp /* check against the existing ACL entries */ 12933965Sjdp entry_id_new = ACL_FIRST_ENTRY; 13033965Sjdp while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { 13133965Sjdp entry_id_new = ACL_NEXT_ENTRY; 13233965Sjdp 13333965Sjdp if (acl_get_tag_type(entry, &tag) == -1) 13433965Sjdp err(1, "%s: acl_get_tag_type() failed", 13533965Sjdp filename); 13633965Sjdp if (acl_get_tag_type(entry_new, &tag_new) == -1) 13733965Sjdp err(1, "%s: acl_get_tag_type() failed", 13833965Sjdp filename); 13933965Sjdp if (tag != tag_new) 14033965Sjdp continue; 14133965Sjdp 14233965Sjdp /* 14333965Sjdp * For NFSv4, in addition to "tag" and "id" we also 14433965Sjdp * compare "entry_type". 14533965Sjdp */ 14633965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 14733965Sjdp if (acl_get_entry_type_np(entry, &entry_type)) 14833965Sjdp err(1, "%s: acl_get_entry_type_np() " 14933965Sjdp "failed", filename); 15033965Sjdp if (acl_get_entry_type_np(entry_new, &entry_type_new)) 15133965Sjdp err(1, "%s: acl_get_entry_type_np() " 15233965Sjdp "failed", filename); 15333965Sjdp if (entry_type != entry_type_new) 15433965Sjdp continue; 15533965Sjdp } 15633965Sjdp 15733965Sjdp switch(tag) { 15833965Sjdp case ACL_USER: 15933965Sjdp case ACL_GROUP: 16033965Sjdp have_entry = merge_user_group(&entry, 16133965Sjdp &entry_new, acl_brand); 16233965Sjdp if (have_entry == 0) 16333965Sjdp break; 16433965Sjdp /* FALLTHROUGH */ 16533965Sjdp case ACL_USER_OBJ: 16633965Sjdp case ACL_GROUP_OBJ: 16733965Sjdp case ACL_OTHER: 16833965Sjdp case ACL_MASK: 16933965Sjdp case ACL_EVERYONE: 17033965Sjdp if (acl_get_permset(entry, &permset) == -1) 17133965Sjdp err(1, "%s: acl_get_permset() failed", 17233965Sjdp filename); 17333965Sjdp if (acl_set_permset(entry_new, permset) == -1) 17433965Sjdp err(1, "%s: acl_set_permset() failed", 17533965Sjdp filename); 17633965Sjdp 17733965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 17833965Sjdp if (acl_get_entry_type_np(entry, &entry_type)) 17933965Sjdp err(1, "%s: acl_get_entry_type_np() failed", 18033965Sjdp filename); 18133965Sjdp if (acl_set_entry_type_np(entry_new, entry_type)) 18233965Sjdp err(1, "%s: acl_set_entry_type_np() failed", 18333965Sjdp filename); 18433965Sjdp if (acl_get_flagset_np(entry, &flagset)) 18533965Sjdp err(1, "%s: acl_get_flagset_np() failed", 18633965Sjdp filename); 18733965Sjdp if (acl_set_flagset_np(entry_new, flagset)) 18833965Sjdp err(1, "%s: acl_set_flagset_np() failed", 18933965Sjdp filename); 19033965Sjdp } 19133965Sjdp had_entry = have_entry = 1; 19233965Sjdp break; 19333965Sjdp default: 19433965Sjdp /* should never be here */ 19533965Sjdp errx(1, "%s: invalid tag type: %i", filename, tag); 19633965Sjdp break; 19733965Sjdp } 19833965Sjdp } 19933965Sjdp 20033965Sjdp /* if this entry has not been found, it must be new */ 20133965Sjdp if (had_entry == 0) { 20233965Sjdp 20333965Sjdp /* 20433965Sjdp * NFSv4 ACL entries must be prepended to the ACL. 20533965Sjdp * Appending them at the end makes no sense, since 20633965Sjdp * in most cases they wouldn't even get evaluated. 20733965Sjdp */ 20833965Sjdp if (acl_brand == ACL_BRAND_NFS4) { 20933965Sjdp if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 21033965Sjdp warn("%s: acl_create_entry_np() failed", filename); 21133965Sjdp acl_free(acl_new); 21233965Sjdp return (-1); 21333965Sjdp } 21433965Sjdp /* 21533965Sjdp * Without this increment, adding several 21633965Sjdp * entries at once, for example 21733965Sjdp * "setfacl -m user:1:r:allow,user:2:r:allow", 21833965Sjdp * would make them appear in reverse order. 21933965Sjdp */ 22033965Sjdp entry_number++; 22133965Sjdp } else { 22233965Sjdp if (acl_create_entry(&acl_new, &entry_new) == -1) { 22333965Sjdp warn("%s: acl_create_entry() failed", filename); 22433965Sjdp acl_free(acl_new); 22533965Sjdp return (-1); 22633965Sjdp } 22733965Sjdp } 22833965Sjdp if (acl_copy_entry(entry_new, entry) == -1) 22933965Sjdp err(1, "%s: acl_copy_entry() failed", filename); 230218822Sdim } 23133965Sjdp } 23233965Sjdp 23333965Sjdp acl_free(*prev_acl); 23433965Sjdp *prev_acl = acl_new; 23533965Sjdp 23633965Sjdp return (0); 23733965Sjdp} 23833965Sjdp 23933965Sjdpint 24033965Sjdpadd_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) 24133965Sjdp{ 24233965Sjdp acl_entry_t entry, entry_new; 24333965Sjdp acl_t acl_new; 24433965Sjdp int entry_id, acl_brand, prev_acl_brand; 24533965Sjdp 24633965Sjdp acl_get_brand_np(acl, &acl_brand); 24733965Sjdp acl_get_brand_np(*prev_acl, &prev_acl_brand); 24833965Sjdp 24933965Sjdp if (prev_acl_brand != ACL_BRAND_NFS4) { 25033965Sjdp warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", 25133965Sjdp filename); 25233965Sjdp return (-1); 25333965Sjdp } 25433965Sjdp 25533965Sjdp if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) { 25633965Sjdp warnx("%s: branding mismatch; existing ACL is NFSv4, " 257218822Sdim "entry to be added is %s", filename, 25833965Sjdp brand_name(acl_brand)); 25933965Sjdp return (-1); 26033965Sjdp } 26133965Sjdp 26233965Sjdp acl_new = acl_dup(*prev_acl); 26333965Sjdp if (acl_new == NULL) 26433965Sjdp err(1, "%s: acl_dup() failed", filename); 26533965Sjdp 26633965Sjdp entry_id = ACL_FIRST_ENTRY; 26733965Sjdp 26833965Sjdp while (acl_get_entry(acl, entry_id, &entry) == 1) { 26933965Sjdp entry_id = ACL_NEXT_ENTRY; 27033965Sjdp 27133965Sjdp if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 27233965Sjdp warn("%s: acl_create_entry_np() failed", filename); 27333965Sjdp acl_free(acl_new); 27433965Sjdp return (-1); 27533965Sjdp } 27633965Sjdp 27733965Sjdp /* 27833965Sjdp * Without this increment, adding several 27933965Sjdp * entries at once, for example 28033965Sjdp * "setfacl -m user:1:r:allow,user:2:r:allow", 28133965Sjdp * would make them appear in reverse order. 28233965Sjdp */ 28333965Sjdp entry_number++; 28433965Sjdp 28533965Sjdp if (acl_copy_entry(entry_new, entry) == -1) 28633965Sjdp err(1, "%s: acl_copy_entry() failed", filename); 28733965Sjdp } 28833965Sjdp 28933965Sjdp acl_free(*prev_acl); 29033965Sjdp *prev_acl = acl_new; 29133965Sjdp 29233965Sjdp return (0); 29333965Sjdp} 29433965Sjdp