Home | History | Annotate | Line # | Download | only in setfacl
      1 /*	$NetBSD: merge.c,v 1.1 2020/05/16 18:31:45 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2001 Chris D. Faulhaber
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 #if 0
     31 __FBSDID("$FreeBSD: head/bin/setfacl/merge.c 333065 2018-04-27 15:25:24Z emaste $");
     32 #else
     33 __RCSID("$NetBSD: merge.c,v 1.1 2020/05/16 18:31:45 christos Exp $");
     34 #endif
     35 
     36 #include <sys/types.h>
     37 #include <sys/acl.h>
     38 #include <sys/stat.h>
     39 
     40 #include <err.h>
     41 #include <stdio.h>
     42 
     43 #include "setfacl.h"
     44 
     45 static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new,
     46     int acl_brand);
     47 
     48 static int
     49 merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand)
     50 {
     51 	acl_permset_t permset;
     52 	acl_entry_type_t entry_type;
     53 	acl_flagset_t flagset;
     54 	int have_entry;
     55 	uid_t *id, *id_new;
     56 
     57 	have_entry = 0;
     58 
     59 	id = acl_get_qualifier(*entry);
     60 	if (id == NULL)
     61 		err(1, "acl_get_qualifier() failed");
     62 	id_new = acl_get_qualifier(*entry_new);
     63 	if (id_new == NULL)
     64 		err(1, "acl_get_qualifier() failed");
     65 	if (*id == *id_new) {
     66 		/* any other matches */
     67 		if (acl_get_permset(*entry, &permset) == -1)
     68 			err(1, "acl_get_permset() failed");
     69 		if (acl_set_permset(*entry_new, permset) == -1)
     70 			err(1, "acl_set_permset() failed");
     71 
     72 		if (acl_brand == ACL_BRAND_NFS4) {
     73 			if (acl_get_entry_type_np(*entry, &entry_type))
     74 				err(1, "acl_get_entry_type_np() failed");
     75 			if (acl_set_entry_type_np(*entry_new, entry_type))
     76 				err(1, "acl_set_entry_type_np() failed");
     77 			if (acl_get_flagset_np(*entry, &flagset))
     78 				err(1, "acl_get_flagset_np() failed");
     79 			if (acl_set_flagset_np(*entry_new, flagset))
     80 				err(1, "acl_set_flagset_np() failed");
     81 		}
     82 
     83 		have_entry = 1;
     84 	}
     85 	acl_free(id);
     86 	acl_free(id_new);
     87 
     88 	return (have_entry);
     89 }
     90 
     91 /*
     92  * merge an ACL into existing file's ACL
     93  */
     94 int
     95 merge_acl(acl_t acl, acl_t *prev_acl, const char *filename)
     96 {
     97 	acl_entry_t entry, entry_new;
     98 	acl_permset_t permset;
     99 	acl_t acl_new;
    100 	acl_tag_t tag, tag_new;
    101 	acl_entry_type_t entry_type, entry_type_new;
    102 	acl_flagset_t flagset;
    103 	int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0;
    104 	int acl_brand, prev_acl_brand;
    105 
    106 	acl_get_brand_np(acl, &acl_brand);
    107 	acl_get_brand_np(*prev_acl, &prev_acl_brand);
    108 
    109 	if (branding_mismatch(acl_brand, prev_acl_brand)) {
    110 		warnx("%s: branding mismatch; existing ACL is %s, "
    111 		    "entry to be merged is %s", filename,
    112 		    brand_name(prev_acl_brand), brand_name(acl_brand));
    113 		return (-1);
    114 	}
    115 
    116 	acl_new = acl_dup(*prev_acl);
    117 	if (acl_new == NULL)
    118 		err(1, "%s: acl_dup() failed", filename);
    119 
    120 	entry_id = ACL_FIRST_ENTRY;
    121 
    122 	while (acl_get_entry(acl, entry_id, &entry) == 1) {
    123 		entry_id = ACL_NEXT_ENTRY;
    124 		have_entry = 0;
    125 		had_entry = 0;
    126 
    127 		/* keep track of existing ACL_MASK entries */
    128 		if (acl_get_tag_type(entry, &tag) == -1)
    129 			err(1, "%s: acl_get_tag_type() failed - "
    130 			    "invalid ACL entry", filename);
    131 		if (tag == ACL_MASK)
    132 			have_mask = true;
    133 
    134 		/* check against the existing ACL entries */
    135 		entry_id_new = ACL_FIRST_ENTRY;
    136 		while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) {
    137 			entry_id_new = ACL_NEXT_ENTRY;
    138 
    139 			if (acl_get_tag_type(entry, &tag) == -1)
    140 				err(1, "%s: acl_get_tag_type() failed",
    141 				    filename);
    142 			if (acl_get_tag_type(entry_new, &tag_new) == -1)
    143 				err(1, "%s: acl_get_tag_type() failed",
    144 				    filename);
    145 			if (tag != tag_new)
    146 				continue;
    147 
    148 			/*
    149 			 * For NFSv4, in addition to "tag" and "id" we also
    150 			 * compare "entry_type".
    151 			 */
    152 			if (acl_brand == ACL_BRAND_NFS4) {
    153 				if (acl_get_entry_type_np(entry, &entry_type))
    154 					err(1, "%s: acl_get_entry_type_np() "
    155 					    "failed", filename);
    156 				if (acl_get_entry_type_np(entry_new, &entry_type_new))
    157 					err(1, "%s: acl_get_entry_type_np() "
    158 					    "failed", filename);
    159 				if (entry_type != entry_type_new)
    160 					continue;
    161 			}
    162 
    163 			switch(tag) {
    164 			case ACL_USER:
    165 			case ACL_GROUP:
    166 				have_entry = merge_user_group(&entry,
    167 				    &entry_new, acl_brand);
    168 				if (have_entry == 0)
    169 					break;
    170 				/* FALLTHROUGH */
    171 			case ACL_USER_OBJ:
    172 			case ACL_GROUP_OBJ:
    173 			case ACL_OTHER:
    174 			case ACL_MASK:
    175 			case ACL_EVERYONE:
    176 				if (acl_get_permset(entry, &permset) == -1)
    177 					err(1, "%s: acl_get_permset() failed",
    178 					    filename);
    179 				if (acl_set_permset(entry_new, permset) == -1)
    180 					err(1, "%s: acl_set_permset() failed",
    181 					    filename);
    182 
    183 				if (acl_brand == ACL_BRAND_NFS4) {
    184 					if (acl_get_entry_type_np(entry, &entry_type))
    185 						err(1, "%s: acl_get_entry_type_np() failed",
    186 						    filename);
    187 					if (acl_set_entry_type_np(entry_new, entry_type))
    188 						err(1, "%s: acl_set_entry_type_np() failed",
    189 						    filename);
    190 					if (acl_get_flagset_np(entry, &flagset))
    191 						err(1, "%s: acl_get_flagset_np() failed",
    192 						    filename);
    193 					if (acl_set_flagset_np(entry_new, flagset))
    194 						err(1, "%s: acl_set_flagset_np() failed",
    195 						    filename);
    196 				}
    197 				had_entry = have_entry = 1;
    198 				break;
    199 			default:
    200 				/* should never be here */
    201 				errx(1, "%s: invalid tag type: %i", filename, tag);
    202 				break;
    203 			}
    204 		}
    205 
    206 		/* if this entry has not been found, it must be new */
    207 		if (had_entry == 0) {
    208 
    209 			/*
    210 			 * NFSv4 ACL entries must be prepended to the ACL.
    211 			 * Appending them at the end makes no sense, since
    212 			 * in most cases they wouldn't even get evaluated.
    213 			 */
    214 			if (acl_brand == ACL_BRAND_NFS4) {
    215 				if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) {
    216 					warn("%s: acl_create_entry_np() failed", filename);
    217 					acl_free(acl_new);
    218 					return (-1);
    219 				}
    220 				/*
    221 				 * Without this increment, adding several
    222 				 * entries at once, for example
    223 				 * "setfacl -m user:1:r:allow,user:2:r:allow",
    224 				 * would make them appear in reverse order.
    225 				 */
    226 				entry_number++;
    227 			} else {
    228 				if (acl_create_entry(&acl_new, &entry_new) == -1) {
    229 					warn("%s: acl_create_entry() failed", filename);
    230 					acl_free(acl_new);
    231 					return (-1);
    232 				}
    233 			}
    234 			if (acl_copy_entry(entry_new, entry) == -1)
    235 				err(1, "%s: acl_copy_entry() failed", filename);
    236 		}
    237 	}
    238 
    239 	acl_free(*prev_acl);
    240 	*prev_acl = acl_new;
    241 
    242 	return (0);
    243 }
    244 
    245 int
    246 add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename)
    247 {
    248 	acl_entry_t entry, entry_new;
    249 	acl_t acl_new;
    250 	int entry_id, acl_brand, prev_acl_brand;
    251 
    252 	acl_get_brand_np(acl, &acl_brand);
    253 	acl_get_brand_np(*prev_acl, &prev_acl_brand);
    254 
    255 	if (prev_acl_brand != ACL_BRAND_NFS4) {
    256 		warnx("%s: the '-a' option is only applicable to NFSv4 ACLs",
    257 		    filename);
    258 		return (-1);
    259 	}
    260 
    261 	if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) {
    262 		warnx("%s: branding mismatch; existing ACL is NFSv4, "
    263 		    "entry to be added is %s", filename,
    264 		    brand_name(acl_brand));
    265 		return (-1);
    266 	}
    267 
    268 	acl_new = acl_dup(*prev_acl);
    269 	if (acl_new == NULL)
    270 		err(1, "%s: acl_dup() failed", filename);
    271 
    272 	entry_id = ACL_FIRST_ENTRY;
    273 
    274 	while (acl_get_entry(acl, entry_id, &entry) == 1) {
    275 		entry_id = ACL_NEXT_ENTRY;
    276 
    277 		if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) {
    278 			warn("%s: acl_create_entry_np() failed", filename);
    279 			acl_free(acl_new);
    280 			return (-1);
    281 		}
    282 
    283 		/*
    284 		 * Without this increment, adding several
    285 		 * entries at once, for example
    286 		 * "setfacl -m user:1:r:allow,user:2:r:allow",
    287 		 * would make them appear in reverse order.
    288 		 */
    289 		entry_number++;
    290 
    291 		if (acl_copy_entry(entry_new, entry) == -1)
    292 			err(1, "%s: acl_copy_entry() failed", filename);
    293 	}
    294 
    295 	acl_free(*prev_acl);
    296 	*prev_acl = acl_new;
    297 
    298 	return (0);
    299 }
    300