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