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