1 /*- 2 * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 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 * $FreeBSD$ 27 */ 28 /* 29 * Support functionality for the POSIX.1e ACL interface 30 * These calls are intended only to be called within the library. 31 */ 32 33 #include <sys/types.h> 34 #include "namespace.h" 35 #include <sys/acl.h> 36 #include "un-namespace.h" 37 #include <errno.h> 38 #include <grp.h> 39 #include <pwd.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 43 #include "acl_support.h" 44 45 #define ACL_STRING_PERM_WRITE 'w' 46 #define ACL_STRING_PERM_READ 'r' 47 #define ACL_STRING_PERM_EXEC 'x' 48 #define ACL_STRING_PERM_NONE '-' 49 50 /* 51 * _posix1e_acl_entry_compare -- compare two acl_entry structures to 52 * determine the order they should appear in. Used by _posix1e_acl_sort to 53 * sort ACL entries into the kernel-desired order -- i.e., the order useful 54 * for evaluation and O(n) validity checking. Beter to have an O(nlogn) sort 55 * in userland and an O(n) in kernel than to have both in kernel. 56 */ 57 typedef int (*compare)(const void *, const void *); 58 static int 59 _posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b) 60 { 61 /* 62 * First, sort between tags -- conveniently defined in the correct 63 * order for verification. 64 */ 65 if (a->ae_tag < b->ae_tag) 66 return (-1); 67 if (a->ae_tag > b->ae_tag) 68 return (1); 69 70 /* 71 * Next compare uids/gids on appropriate types. 72 */ 73 74 if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) { 75 if (a->ae_id < b->ae_id) 76 return (-1); 77 if (a->ae_id > b->ae_id) 78 return (1); 79 80 /* shouldn't be equal, fall through to the invalid case */ 81 } 82 83 /* 84 * Don't know how to sort multiple entries of the rest--either it's 85 * a bad entry, or there shouldn't be more than one. Ignore and the 86 * validity checker can get it later. 87 */ 88 return (0); 89 } 90 91 /* 92 * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs 93 * Give the opportunity to fail, althouh we don't currently have a way 94 * to fail. 95 */ 96 int 97 _posix1e_acl_sort(acl_t acl) 98 { 99 100 qsort(&acl->acl_entry[0], acl->acl_cnt, sizeof(struct acl_entry), 101 (compare) _posix1e_acl_entry_compare); 102 103 return (0); 104 } 105 106 /* 107 * acl_posix1e -- in what situations should we acl_sort before submission? 108 * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or 109 * ACL_TYPE_DEFAULT 110 */ 111 int 112 _posix1e_acl(acl_t acl, acl_type_t type) 113 { 114 115 return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT)); 116 } 117 118 /* 119 * _posix1e_acl_check -- given an ACL, check its validity. This is mirrored 120 * from code in sys/kern/kern_acl.c, and if changes are made in one, they 121 * should be made in the other also. This copy of acl_check is made 122 * available * in userland for the benefit of processes wanting to check ACLs 123 * for validity before submitting them to the kernel, or for performing 124 * in userland file system checking. Needless to say, the kernel makes 125 * the real checks on calls to get/setacl. 126 * 127 * See the comments in kernel for explanation -- just briefly, it assumes 128 * an already sorted ACL, and checks based on that assumption. The 129 * POSIX.1e interface, acl_valid(), will perform the sort before calling 130 * this. Returns 0 on success, EINVAL on failure. 131 */ 132 int 133 _posix1e_acl_check(struct acl *acl) 134 { 135 struct acl_entry *entry; /* current entry */ 136 uid_t obj_uid=-1, obj_gid=-1, highest_uid=0, highest_gid=0; 137 int stage = ACL_USER_OBJ; 138 int i = 0; 139 int count_user_obj=0, count_user=0, count_group_obj=0, 140 count_group=0, count_mask=0, count_other=0; 141 142 /* printf("_posix1e_acl_check: checking acl with %d entries\n", 143 acl->acl_cnt); */ 144 while (i < acl->acl_cnt) { 145 entry = &acl->acl_entry[i]; 146 147 if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS) 148 return (EINVAL); 149 150 switch(entry->ae_tag) { 151 case ACL_USER_OBJ: 152 /* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n", 153 i); */ 154 if (stage > ACL_USER_OBJ) 155 return (EINVAL); 156 stage = ACL_USER; 157 count_user_obj++; 158 obj_uid = entry->ae_id; 159 break; 160 161 case ACL_USER: 162 /* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */ 163 if (stage > ACL_USER) 164 return (EINVAL); 165 stage = ACL_USER; 166 if (entry->ae_id == obj_uid) 167 return (EINVAL); 168 if (count_user && (entry->ae_id <= highest_uid)) 169 return (EINVAL); 170 highest_uid = entry->ae_id; 171 count_user++; 172 break; 173 174 case ACL_GROUP_OBJ: 175 /* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n", 176 i); */ 177 if (stage > ACL_GROUP_OBJ) 178 return (EINVAL); 179 stage = ACL_GROUP; 180 count_group_obj++; 181 obj_gid = entry->ae_id; 182 break; 183 184 case ACL_GROUP: 185 /* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */ 186 if (stage > ACL_GROUP) 187 return (EINVAL); 188 stage = ACL_GROUP; 189 if (entry->ae_id == obj_gid) 190 return (EINVAL); 191 if (count_group && (entry->ae_id <= highest_gid)) 192 return (EINVAL); 193 highest_gid = entry->ae_id; 194 count_group++; 195 break; 196 197 case ACL_MASK: 198 /* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */ 199 if (stage > ACL_MASK) 200 return (EINVAL); 201 stage = ACL_MASK; 202 count_mask++; 203 break; 204 205 case ACL_OTHER: 206 /* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */ 207 if (stage > ACL_OTHER) 208 return (EINVAL); 209 stage = ACL_OTHER; 210 count_other++; 211 break; 212 213 default: 214 /* printf("_posix1e_acl_check: %d: INVALID\n", i); */ 215 return (EINVAL); 216 } 217 i++; 218 } 219 220 if (count_user_obj != 1) 221 return (EINVAL); 222 223 if (count_group_obj != 1) 224 return (EINVAL); 225 226 if (count_mask != 0 && count_mask != 1) 227 return (EINVAL); 228 229 if (count_other != 1) 230 return (EINVAL); 231 232 return (0); 233 } 234 235 236 /* 237 * Given a uid/gid, return a username/groupname for the text form of an ACL 238 * XXX NOT THREAD SAFE, RELIES ON GETPWUID, GETGRGID 239 * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 240 * MAY HAVE SIDE-EFFECTS 241 */ 242 int 243 _posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf) 244 { 245 struct group *g; 246 struct passwd *p; 247 int i; 248 249 switch(tag) { 250 case ACL_USER: 251 p = getpwuid(id); 252 if (!p) 253 i = snprintf(buf, buf_len, "%d", id); 254 else 255 i = snprintf(buf, buf_len, "%s", p->pw_name); 256 257 if (i >= buf_len) { 258 errno = ENOMEM; 259 return (-1); 260 } 261 return (0); 262 263 case ACL_GROUP: 264 g = getgrgid(id); 265 if (!g) 266 i = snprintf(buf, buf_len, "%d", id); 267 else 268 i = snprintf(buf, buf_len, "%s", g->gr_name); 269 270 if (i >= buf_len) { 271 errno = ENOMEM; 272 return (-1); 273 } 274 return (0); 275 276 default: 277 return (EINVAL); 278 } 279 } 280 281 282 /* 283 * Given a username/groupname from a text form of an ACL, return the uid/gid 284 * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM 285 * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE 286 * MAY HAVE SIDE-EFFECTS 287 * 288 * XXX currently doesn't deal correctly with a numeric uid being passed 289 * instead of a username. What is correct behavior here? Check chown. 290 */ 291 int 292 _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id) 293 { 294 struct group *g; 295 struct passwd *p; 296 unsigned long l; 297 char *endp; 298 299 switch(tag) { 300 case ACL_USER: 301 p = getpwnam(name); 302 if (p == NULL) { 303 l = strtoul(name, &endp, 0); 304 if (*endp != '\0' || l != (unsigned long)(uid_t)l) { 305 errno = EINVAL; 306 return (-1); 307 } 308 *id = (uid_t)l; 309 return (0); 310 } 311 *id = p->pw_uid; 312 return (0); 313 314 case ACL_GROUP: 315 g = getgrnam(name); 316 if (g == NULL) { 317 l = strtoul(name, &endp, 0); 318 if (*endp != '\0' || l != (unsigned long)(gid_t)l) { 319 errno = EINVAL; 320 return (-1); 321 } 322 *id = (gid_t)l; 323 return (0); 324 } 325 *id = g->gr_gid; 326 return (0); 327 328 default: 329 return (EINVAL); 330 } 331 } 332 333 334 /* 335 * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill 336 * in a string describing the permissions. 337 */ 338 int 339 _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf) 340 { 341 342 if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) { 343 errno = ENOMEM; 344 return (-1); 345 } 346 347 if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) { 348 errno = EINVAL; 349 return (-1); 350 } 351 352 buf[3] = 0; /* null terminate */ 353 354 if (perm & ACL_READ) 355 buf[0] = ACL_STRING_PERM_READ; 356 else 357 buf[0] = ACL_STRING_PERM_NONE; 358 359 if (perm & ACL_WRITE) 360 buf[1] = ACL_STRING_PERM_WRITE; 361 else 362 buf[1] = ACL_STRING_PERM_NONE; 363 364 if (perm & ACL_EXECUTE) 365 buf[2] = ACL_STRING_PERM_EXEC; 366 else 367 buf[2] = ACL_STRING_PERM_NONE; 368 369 return (0); 370 } 371 372 /* 373 * given a string, return a permission describing it 374 */ 375 int 376 _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm) 377 { 378 acl_perm_t myperm = ACL_PERM_NONE; 379 char *ch; 380 381 ch = string; 382 while (*ch) { 383 switch(*ch) { 384 case ACL_STRING_PERM_READ: 385 myperm |= ACL_READ; 386 break; 387 case ACL_STRING_PERM_WRITE: 388 myperm |= ACL_WRITE; 389 break; 390 case ACL_STRING_PERM_EXEC: 391 myperm |= ACL_EXECUTE; 392 break; 393 case ACL_STRING_PERM_NONE: 394 break; 395 default: 396 return (EINVAL); 397 } 398 ch++; 399 } 400 401 *perm = myperm; 402 return (0); 403 } 404 405 /* 406 * Add an ACL entry without doing much checking, et al 407 */ 408 int 409 _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm) 410 { 411 struct acl_entry *e; 412 413 if (acl->acl_cnt >= ACL_MAX_ENTRIES) { 414 errno = ENOMEM; 415 return (-1); 416 } 417 418 e = &(acl->acl_entry[acl->acl_cnt]); 419 e->ae_perm = perm; 420 e->ae_tag = tag; 421 e->ae_id = id; 422 acl->acl_cnt++; 423 424 return (0); 425 } 426