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