1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/types.h> 30 #include <sys/acl.h> 31 #include <sys/stat.h> 32 33 #include <err.h> 34 35 #include "setfacl.h" 36 37 static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, 38 int acl_brand); 39 40 static int 41 merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) 42 { 43 acl_permset_t permset; 44 acl_entry_type_t entry_type; 45 acl_flagset_t flagset; 46 int have_entry; 47 uid_t *id, *id_new; 48 49 have_entry = 0; 50 51 id = acl_get_qualifier(*entry); 52 if (id == NULL) 53 err(1, "acl_get_qualifier() failed"); 54 id_new = acl_get_qualifier(*entry_new); 55 if (id_new == NULL) 56 err(1, "acl_get_qualifier() failed"); 57 if (*id == *id_new) { 58 /* any other matches */ 59 if (acl_get_permset(*entry, &permset) == -1) 60 err(1, "acl_get_permset() failed"); 61 if (acl_set_permset(*entry_new, permset) == -1) 62 err(1, "acl_set_permset() failed"); 63 64 if (acl_brand == ACL_BRAND_NFS4) { 65 if (acl_get_entry_type_np(*entry, &entry_type)) 66 err(1, "acl_get_entry_type_np() failed"); 67 if (acl_set_entry_type_np(*entry_new, entry_type)) 68 err(1, "acl_set_entry_type_np() failed"); 69 if (acl_get_flagset_np(*entry, &flagset)) 70 err(1, "acl_get_flagset_np() failed"); 71 if (acl_set_flagset_np(*entry_new, flagset)) 72 err(1, "acl_set_flagset_np() failed"); 73 } 74 75 have_entry = 1; 76 } 77 acl_free(id); 78 acl_free(id_new); 79 80 return (have_entry); 81 } 82 83 /* 84 * merge an ACL into existing file's ACL 85 */ 86 int 87 merge_acl(acl_t acl, acl_t *prev_acl, const char *filename) 88 { 89 acl_entry_t entry, entry_new; 90 acl_permset_t permset; 91 acl_t acl_new; 92 acl_tag_t tag, tag_new; 93 acl_entry_type_t entry_type, entry_type_new; 94 acl_flagset_t flagset; 95 int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0; 96 int acl_brand, prev_acl_brand; 97 98 acl_get_brand_np(acl, &acl_brand); 99 acl_get_brand_np(*prev_acl, &prev_acl_brand); 100 101 if (branding_mismatch(acl_brand, prev_acl_brand)) { 102 warnx("%s: branding mismatch; existing ACL is %s, " 103 "entry to be merged is %s", filename, 104 brand_name(prev_acl_brand), brand_name(acl_brand)); 105 return (-1); 106 } 107 108 acl_new = acl_dup(*prev_acl); 109 if (acl_new == NULL) 110 err(1, "%s: acl_dup() failed", filename); 111 112 entry_id = ACL_FIRST_ENTRY; 113 114 while (acl_get_entry(acl, entry_id, &entry) == 1) { 115 entry_id = ACL_NEXT_ENTRY; 116 have_entry = 0; 117 had_entry = 0; 118 119 /* keep track of existing ACL_MASK entries */ 120 if (acl_get_tag_type(entry, &tag) == -1) 121 err(1, "%s: acl_get_tag_type() failed - " 122 "invalid ACL entry", filename); 123 if (tag == ACL_MASK) 124 have_mask = true; 125 126 /* check against the existing ACL entries */ 127 entry_id_new = ACL_FIRST_ENTRY; 128 while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { 129 entry_id_new = ACL_NEXT_ENTRY; 130 131 if (acl_get_tag_type(entry, &tag) == -1) 132 err(1, "%s: acl_get_tag_type() failed", 133 filename); 134 if (acl_get_tag_type(entry_new, &tag_new) == -1) 135 err(1, "%s: acl_get_tag_type() failed", 136 filename); 137 if (tag != tag_new) 138 continue; 139 140 /* 141 * For NFSv4, in addition to "tag" and "id" we also 142 * compare "entry_type". 143 */ 144 if (acl_brand == ACL_BRAND_NFS4) { 145 if (acl_get_entry_type_np(entry, &entry_type)) 146 err(1, "%s: acl_get_entry_type_np() " 147 "failed", filename); 148 if (acl_get_entry_type_np(entry_new, &entry_type_new)) 149 err(1, "%s: acl_get_entry_type_np() " 150 "failed", filename); 151 if (entry_type != entry_type_new) 152 continue; 153 } 154 155 switch(tag) { 156 case ACL_USER: 157 case ACL_GROUP: 158 have_entry = merge_user_group(&entry, 159 &entry_new, acl_brand); 160 if (have_entry == 0) 161 break; 162 /* FALLTHROUGH */ 163 case ACL_USER_OBJ: 164 case ACL_GROUP_OBJ: 165 case ACL_OTHER: 166 case ACL_MASK: 167 case ACL_EVERYONE: 168 if (acl_get_permset(entry, &permset) == -1) 169 err(1, "%s: acl_get_permset() failed", 170 filename); 171 if (acl_set_permset(entry_new, permset) == -1) 172 err(1, "%s: acl_set_permset() failed", 173 filename); 174 175 if (acl_brand == ACL_BRAND_NFS4) { 176 if (acl_get_entry_type_np(entry, &entry_type)) 177 err(1, "%s: acl_get_entry_type_np() failed", 178 filename); 179 if (acl_set_entry_type_np(entry_new, entry_type)) 180 err(1, "%s: acl_set_entry_type_np() failed", 181 filename); 182 if (acl_get_flagset_np(entry, &flagset)) 183 err(1, "%s: acl_get_flagset_np() failed", 184 filename); 185 if (acl_set_flagset_np(entry_new, flagset)) 186 err(1, "%s: acl_set_flagset_np() failed", 187 filename); 188 } 189 had_entry = have_entry = 1; 190 break; 191 default: 192 /* should never be here */ 193 errx(1, "%s: invalid tag type: %i", filename, tag); 194 break; 195 } 196 } 197 198 /* if this entry has not been found, it must be new */ 199 if (had_entry == 0) { 200 201 /* 202 * NFSv4 ACL entries must be prepended to the ACL. 203 * Appending them at the end makes no sense, since 204 * in most cases they wouldn't even get evaluated. 205 */ 206 if (acl_brand == ACL_BRAND_NFS4) { 207 if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 208 warn("%s: acl_create_entry_np() failed", filename); 209 acl_free(acl_new); 210 return (-1); 211 } 212 /* 213 * Without this increment, adding several 214 * entries at once, for example 215 * "setfacl -m user:1:r:allow,user:2:r:allow", 216 * would make them appear in reverse order. 217 */ 218 entry_number++; 219 } else { 220 if (acl_create_entry(&acl_new, &entry_new) == -1) { 221 warn("%s: acl_create_entry() failed", filename); 222 acl_free(acl_new); 223 return (-1); 224 } 225 } 226 if (acl_copy_entry(entry_new, entry) == -1) 227 err(1, "%s: acl_copy_entry() failed", filename); 228 } 229 } 230 231 acl_free(*prev_acl); 232 *prev_acl = acl_new; 233 234 return (0); 235 } 236 237 int 238 add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) 239 { 240 acl_entry_t entry, entry_new; 241 acl_t acl_new; 242 int entry_id, acl_brand, prev_acl_brand; 243 244 acl_get_brand_np(acl, &acl_brand); 245 acl_get_brand_np(*prev_acl, &prev_acl_brand); 246 247 if (prev_acl_brand != ACL_BRAND_NFS4) { 248 warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", 249 filename); 250 return (-1); 251 } 252 253 if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) { 254 warnx("%s: branding mismatch; existing ACL is NFSv4, " 255 "entry to be added is %s", filename, 256 brand_name(acl_brand)); 257 return (-1); 258 } 259 260 acl_new = acl_dup(*prev_acl); 261 if (acl_new == NULL) 262 err(1, "%s: acl_dup() failed", filename); 263 264 entry_id = ACL_FIRST_ENTRY; 265 266 while (acl_get_entry(acl, entry_id, &entry) == 1) { 267 entry_id = ACL_NEXT_ENTRY; 268 269 if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { 270 warn("%s: acl_create_entry_np() failed", filename); 271 acl_free(acl_new); 272 return (-1); 273 } 274 275 /* 276 * Without this increment, adding several 277 * entries at once, for example 278 * "setfacl -m user:1:r:allow,user:2:r:allow", 279 * would make them appear in reverse order. 280 */ 281 entry_number++; 282 283 if (acl_copy_entry(entry_new, entry) == -1) 284 err(1, "%s: acl_copy_entry() failed", filename); 285 } 286 287 acl_free(*prev_acl); 288 *prev_acl = acl_new; 289 290 return (0); 291 } 292