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