|
|
1.1 ! root 1: /* ! 2: * $Source: /usr/src/kerberosIV/acl/RCS/acl_files.c,v $ ! 3: * $Author: kfall $ ! 4: * ! 5: * Copyright 1987,1989 by the Massachusetts Institute of Technology. ! 6: * ! 7: * For copying and distribution information, please see the file ! 8: * <mit-copyright.h>. ! 9: * ! 10: */ ! 11: ! 12: #ifndef lint ! 13: static char rcsid_acl_files_c[] = "$Id: acl_files.c,v 4.5 90/06/25 20:58:42 kfall Exp $"; ! 14: #endif lint ! 15: ! 16: ! 17: /*** Routines for manipulating access control list files ***/ ! 18: ! 19: #include <stdio.h> ! 20: #include <strings.h> ! 21: #include <sys/file.h> ! 22: #include <sys/types.h> ! 23: #include <sys/stat.h> ! 24: #include <sys/errno.h> ! 25: #include <ctype.h> ! 26: #include "des.h" ! 27: #include "krb.h" ! 28: ! 29: #ifndef KRB_REALM ! 30: #define KRB_REALM "CS.BERKELEY.EDU" ! 31: #endif ! 32: ! 33: /* "aname.inst@realm" */ ! 34: #define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) ! 35: #define INST_SEP '.' ! 36: #define REALM_SEP '@' ! 37: ! 38: #define LINESIZE 2048 /* Maximum line length in an acl file */ ! 39: ! 40: #define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ ! 41: #define WAIT_TIME 300 /* Maximum time allowed write acl file */ ! 42: ! 43: #define CACHED_ACLS 8 /* How many acls to cache */ ! 44: /* Each acl costs 1 open file descriptor */ ! 45: #define ACL_LEN 16 /* Twice a reasonable acl length */ ! 46: ! 47: #define MAX(a,b) (((a)>(b))?(a):(b)) ! 48: #define MIN(a,b) (((a)<(b))?(a):(b)) ! 49: ! 50: #define COR(a,b) ((a!=NULL)?(a):(b)) ! 51: ! 52: extern int errno; ! 53: ! 54: extern char *malloc(), *calloc(); ! 55: extern time_t time(); ! 56: ! 57: /* Canonicalize a principal name */ ! 58: /* If instance is missing, it becomes "" */ ! 59: /* If realm is missing, it becomes the local realm */ ! 60: /* Canonicalized form is put in canon, which must be big enough to hold ! 61: MAX_PRINCIPAL_SIZE characters */ ! 62: acl_canonicalize_principal(principal, canon) ! 63: char *principal; ! 64: char *canon; ! 65: { ! 66: char *dot, *atsign, *end; ! 67: int len; ! 68: ! 69: dot = index(principal, INST_SEP); ! 70: atsign = index(principal, REALM_SEP); ! 71: ! 72: /* Maybe we're done already */ ! 73: if(dot != NULL && atsign != NULL) { ! 74: if(dot < atsign) { ! 75: /* It's for real */ ! 76: /* Copy into canon */ ! 77: strncpy(canon, principal, MAX_PRINCIPAL_SIZE); ! 78: canon[MAX_PRINCIPAL_SIZE-1] = '\0'; ! 79: return; ! 80: } else { ! 81: /* Nope, it's part of the realm */ ! 82: dot = NULL; ! 83: } ! 84: } ! 85: ! 86: /* No such luck */ ! 87: end = principal + strlen(principal); ! 88: ! 89: /* Get the principal name */ ! 90: len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); ! 91: strncpy(canon, principal, len); ! 92: canon += len; ! 93: ! 94: /* Add INST_SEP */ ! 95: *canon++ = INST_SEP; ! 96: ! 97: /* Get the instance, if it exists */ ! 98: if(dot != NULL) { ! 99: ++dot; ! 100: len = MIN(INST_SZ, COR(atsign, end) - dot); ! 101: strncpy(canon, dot, len); ! 102: canon += len; ! 103: } ! 104: ! 105: /* Add REALM_SEP */ ! 106: *canon++ = REALM_SEP; ! 107: ! 108: /* Get the realm, if it exists */ ! 109: /* Otherwise, default to local realm */ ! 110: if(atsign != NULL) { ! 111: ++atsign; ! 112: len = MIN(REALM_SZ, end - atsign); ! 113: strncpy(canon, atsign, len); ! 114: canon += len; ! 115: *canon++ = '\0'; ! 116: } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { ! 117: strcpy(canon, KRB_REALM); ! 118: } ! 119: } ! 120: ! 121: /* Get a lock to modify acl_file */ ! 122: /* Return new FILE pointer */ ! 123: /* or NULL if file cannot be modified */ ! 124: /* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ ! 125: static FILE *acl_lock_file(acl_file) ! 126: char *acl_file; ! 127: { ! 128: struct stat s; ! 129: char new[LINESIZE]; ! 130: int nfd; ! 131: FILE *nf; ! 132: int mode; ! 133: ! 134: if(stat(acl_file, &s) < 0) return(NULL); ! 135: mode = s.st_mode; ! 136: sprintf(new, NEW_FILE, acl_file); ! 137: for(;;) { ! 138: /* Open the new file */ ! 139: if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { ! 140: if(errno == EEXIST) { ! 141: /* Maybe somebody got here already, maybe it's just old */ ! 142: if(stat(new, &s) < 0) return(NULL); ! 143: if(time(0) - s.st_ctime > WAIT_TIME) { ! 144: /* File is stale, kill it */ ! 145: unlink(new); ! 146: continue; ! 147: } else { ! 148: /* Wait and try again */ ! 149: sleep(1); ! 150: continue; ! 151: } ! 152: } else { ! 153: /* Some other error, we lose */ ! 154: return(NULL); ! 155: } ! 156: } ! 157: ! 158: /* If we got to here, the lock file is ours and ok */ ! 159: /* Reopen it under stdio */ ! 160: if((nf = fdopen(nfd, "w")) == NULL) { ! 161: /* Oops, clean up */ ! 162: unlink(new); ! 163: } ! 164: return(nf); ! 165: } ! 166: } ! 167: ! 168: /* Commit changes to acl_file written onto FILE *f */ ! 169: /* Returns zero if successful */ ! 170: /* Returns > 0 if lock was broken */ ! 171: /* Returns < 0 if some other error occurs */ ! 172: /* Closes f */ ! 173: static int acl_commit(acl_file, f) ! 174: char *acl_file; ! 175: FILE *f; ! 176: { ! 177: char new[LINESIZE]; ! 178: int ret; ! 179: struct stat s; ! 180: ! 181: sprintf(new, NEW_FILE, acl_file); ! 182: if(fflush(f) < 0 ! 183: || fstat(fileno(f), &s) < 0 ! 184: || s.st_nlink == 0) { ! 185: acl_abort(acl_file, f); ! 186: return(-1); ! 187: } ! 188: ! 189: ret = rename(new, acl_file); ! 190: fclose(f); ! 191: return(ret); ! 192: } ! 193: ! 194: /* Abort changes to acl_file written onto FILE *f */ ! 195: /* Returns 0 if successful, < 0 otherwise */ ! 196: /* Closes f */ ! 197: static int acl_abort(acl_file, f) ! 198: char *acl_file; ! 199: FILE *f; ! 200: { ! 201: char new[LINESIZE]; ! 202: int ret; ! 203: struct stat s; ! 204: ! 205: /* make sure we aren't nuking someone else's file */ ! 206: if(fstat(fileno(f), &s) < 0 ! 207: || s.st_nlink == 0) { ! 208: fclose(f); ! 209: return(-1); ! 210: } else { ! 211: sprintf(new, NEW_FILE, acl_file); ! 212: ret = unlink(new); ! 213: fclose(f); ! 214: return(ret); ! 215: } ! 216: } ! 217: ! 218: /* Initialize an acl_file */ ! 219: /* Creates the file with permissions perm if it does not exist */ ! 220: /* Erases it if it does */ ! 221: /* Returns return value of acl_commit */ ! 222: int acl_initialize(acl_file, perm) ! 223: char *acl_file; ! 224: int perm; ! 225: { ! 226: FILE *new; ! 227: int fd; ! 228: ! 229: /* Check if the file exists already */ ! 230: if((new = acl_lock_file(acl_file)) != NULL) { ! 231: return(acl_commit(acl_file, new)); ! 232: } else { ! 233: /* File must be readable and writable by owner */ ! 234: if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { ! 235: return(-1); ! 236: } else { ! 237: close(fd); ! 238: return(0); ! 239: } ! 240: } ! 241: } ! 242: ! 243: /* Eliminate all whitespace character in buf */ ! 244: /* Modifies its argument */ ! 245: static nuke_whitespace(buf) ! 246: char *buf; ! 247: { ! 248: register char *pin, *pout; ! 249: ! 250: for(pin = pout = buf; *pin != '\0'; pin++) ! 251: if(!isspace(*pin)) *pout++ = *pin; ! 252: *pout = '\0'; /* Terminate the string */ ! 253: } ! 254: ! 255: /* Hash table stuff */ ! 256: ! 257: struct hashtbl { ! 258: int size; /* Max number of entries */ ! 259: int entries; /* Actual number of entries */ ! 260: char **tbl; /* Pointer to start of table */ ! 261: }; ! 262: ! 263: /* Make an empty hash table of size s */ ! 264: static struct hashtbl *make_hash(size) ! 265: int size; ! 266: { ! 267: struct hashtbl *h; ! 268: ! 269: if(size < 1) size = 1; ! 270: h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); ! 271: h->size = size; ! 272: h->entries = 0; ! 273: h->tbl = (char **) calloc(size, sizeof(char *)); ! 274: return(h); ! 275: } ! 276: ! 277: /* Destroy a hash table */ ! 278: static destroy_hash(h) ! 279: struct hashtbl *h; ! 280: { ! 281: int i; ! 282: ! 283: for(i = 0; i < h->size; i++) { ! 284: if(h->tbl[i] != NULL) free(h->tbl[i]); ! 285: } ! 286: free(h->tbl); ! 287: free(h); ! 288: } ! 289: ! 290: /* Compute hash value for a string */ ! 291: static unsigned hashval(s) ! 292: register char *s; ! 293: { ! 294: register unsigned hv; ! 295: ! 296: for(hv = 0; *s != '\0'; s++) { ! 297: hv ^= ((hv << 3) ^ *s); ! 298: } ! 299: return(hv); ! 300: } ! 301: ! 302: /* Add an element to a hash table */ ! 303: static add_hash(h, el) ! 304: struct hashtbl *h; ! 305: char *el; ! 306: { ! 307: unsigned hv; ! 308: char *s; ! 309: char **old; ! 310: int i; ! 311: ! 312: /* Make space if it isn't there already */ ! 313: if(h->entries + 1 > (h->size >> 1)) { ! 314: old = h->tbl; ! 315: h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); ! 316: for(i = 0; i < h->size; i++) { ! 317: if(old[i] != NULL) { ! 318: hv = hashval(old[i]) % (h->size << 1); ! 319: while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); ! 320: h->tbl[hv] = old[i]; ! 321: } ! 322: } ! 323: h->size = h->size << 1; ! 324: free(old); ! 325: } ! 326: ! 327: hv = hashval(el) % h->size; ! 328: while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; ! 329: s = malloc(strlen(el)+1); ! 330: strcpy(s, el); ! 331: h->tbl[hv] = s; ! 332: h->entries++; ! 333: } ! 334: ! 335: /* Returns nonzero if el is in h */ ! 336: static check_hash(h, el) ! 337: struct hashtbl *h; ! 338: char *el; ! 339: { ! 340: unsigned hv; ! 341: ! 342: for(hv = hashval(el) % h->size; ! 343: h->tbl[hv] != NULL; ! 344: hv = (hv + 1) % h->size) { ! 345: if(!strcmp(h->tbl[hv], el)) return(1); ! 346: } ! 347: return(0); ! 348: } ! 349: ! 350: struct acl { ! 351: char filename[LINESIZE]; /* Name of acl file */ ! 352: int fd; /* File descriptor for acl file */ ! 353: struct stat status; /* File status at last read */ ! 354: struct hashtbl *acl; /* Acl entries */ ! 355: }; ! 356: ! 357: static struct acl acl_cache[CACHED_ACLS]; ! 358: ! 359: static int acl_cache_count = 0; ! 360: static int acl_cache_next = 0; ! 361: ! 362: /* Returns < 0 if unsuccessful in loading acl */ ! 363: /* Returns index into acl_cache otherwise */ ! 364: /* Note that if acl is already loaded, this is just a lookup */ ! 365: static int acl_load(name) ! 366: char *name; ! 367: { ! 368: int i; ! 369: FILE *f; ! 370: struct stat s; ! 371: char buf[MAX_PRINCIPAL_SIZE]; ! 372: char canon[MAX_PRINCIPAL_SIZE]; ! 373: ! 374: /* See if it's there already */ ! 375: for(i = 0; i < acl_cache_count; i++) { ! 376: if(!strcmp(acl_cache[i].filename, name) ! 377: && acl_cache[i].fd >= 0) goto got_it; ! 378: } ! 379: ! 380: /* It isn't, load it in */ ! 381: /* maybe there's still room */ ! 382: if(acl_cache_count < CACHED_ACLS) { ! 383: i = acl_cache_count++; ! 384: } else { ! 385: /* No room, clean one out */ ! 386: i = acl_cache_next; ! 387: acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; ! 388: close(acl_cache[i].fd); ! 389: if(acl_cache[i].acl) { ! 390: destroy_hash(acl_cache[i].acl); ! 391: acl_cache[i].acl = (struct hashtbl *) 0; ! 392: } ! 393: } ! 394: ! 395: /* Set up the acl */ ! 396: strcpy(acl_cache[i].filename, name); ! 397: if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); ! 398: /* Force reload */ ! 399: acl_cache[i].acl = (struct hashtbl *) 0; ! 400: ! 401: got_it: ! 402: /* ! 403: * See if the stat matches ! 404: * ! 405: * Use stat(), not fstat(), as the file may have been re-created by ! 406: * acl_add or acl_delete. If this happens, the old inode will have ! 407: * no changes in the mod-time and the following test will fail. ! 408: */ ! 409: if(stat(acl_cache[i].filename, &s) < 0) return(-1); ! 410: if(acl_cache[i].acl == (struct hashtbl *) 0 ! 411: || s.st_nlink != acl_cache[i].status.st_nlink ! 412: || s.st_mtime != acl_cache[i].status.st_mtime ! 413: || s.st_ctime != acl_cache[i].status.st_ctime) { ! 414: /* Gotta reload */ ! 415: if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); ! 416: if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); ! 417: if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); ! 418: if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); ! 419: acl_cache[i].acl = make_hash(ACL_LEN); ! 420: while(fgets(buf, sizeof(buf), f) != NULL) { ! 421: nuke_whitespace(buf); ! 422: acl_canonicalize_principal(buf, canon); ! 423: add_hash(acl_cache[i].acl, canon); ! 424: } ! 425: fclose(f); ! 426: acl_cache[i].status = s; ! 427: } ! 428: return(i); ! 429: } ! 430: ! 431: /* Returns nonzero if it can be determined that acl contains principal */ ! 432: /* Principal is not canonicalized, and no wildcarding is done */ ! 433: acl_exact_match(acl, principal) ! 434: char *acl; ! 435: char *principal; ! 436: { ! 437: int idx; ! 438: ! 439: return((idx = acl_load(acl)) >= 0 ! 440: && check_hash(acl_cache[idx].acl, principal)); ! 441: } ! 442: ! 443: /* Returns nonzero if it can be determined that acl contains principal */ ! 444: /* Recognizes wildcards in acl of the form ! 445: name.*@realm, *.*@realm, and *.*@* */ ! 446: acl_check(acl, principal) ! 447: char *acl; ! 448: char *principal; ! 449: { ! 450: char buf[MAX_PRINCIPAL_SIZE]; ! 451: char canon[MAX_PRINCIPAL_SIZE]; ! 452: char *realm; ! 453: ! 454: acl_canonicalize_principal(principal, canon); ! 455: ! 456: /* Is it there? */ ! 457: if(acl_exact_match(acl, canon)) return(1); ! 458: ! 459: /* Try the wildcards */ ! 460: realm = index(canon, REALM_SEP); ! 461: *index(canon, INST_SEP) = '\0'; /* Chuck the instance */ ! 462: ! 463: sprintf(buf, "%s.*%s", canon, realm); ! 464: if(acl_exact_match(acl, buf)) return(1); ! 465: ! 466: sprintf(buf, "*.*%s", realm); ! 467: if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); ! 468: ! 469: return(0); ! 470: } ! 471: ! 472: /* Adds principal to acl */ ! 473: /* Wildcards are interpreted literally */ ! 474: acl_add(acl, principal) ! 475: char *acl; ! 476: char *principal; ! 477: { ! 478: int idx; ! 479: int i; ! 480: FILE *new; ! 481: char canon[MAX_PRINCIPAL_SIZE]; ! 482: ! 483: acl_canonicalize_principal(principal, canon); ! 484: ! 485: if((new = acl_lock_file(acl)) == NULL) return(-1); ! 486: if((acl_exact_match(acl, canon)) ! 487: || (idx = acl_load(acl)) < 0) { ! 488: acl_abort(acl, new); ! 489: return(-1); ! 490: } ! 491: /* It isn't there yet, copy the file and put it in */ ! 492: for(i = 0; i < acl_cache[idx].acl->size; i++) { ! 493: if(acl_cache[idx].acl->tbl[i] != NULL) { ! 494: if(fputs(acl_cache[idx].acl->tbl[i], new) == NULL ! 495: || putc('\n', new) != '\n') { ! 496: acl_abort(acl, new); ! 497: return(-1); ! 498: } ! 499: } ! 500: } ! 501: fputs(canon, new); ! 502: putc('\n', new); ! 503: return(acl_commit(acl, new)); ! 504: } ! 505: ! 506: /* Removes principal from acl */ ! 507: /* Wildcards are interpreted literally */ ! 508: acl_delete(acl, principal) ! 509: char *acl; ! 510: char *principal; ! 511: { ! 512: int idx; ! 513: int i; ! 514: FILE *new; ! 515: char canon[MAX_PRINCIPAL_SIZE]; ! 516: ! 517: acl_canonicalize_principal(principal, canon); ! 518: ! 519: if((new = acl_lock_file(acl)) == NULL) return(-1); ! 520: if((!acl_exact_match(acl, canon)) ! 521: || (idx = acl_load(acl)) < 0) { ! 522: acl_abort(acl, new); ! 523: return(-1); ! 524: } ! 525: /* It isn't there yet, copy the file and put it in */ ! 526: for(i = 0; i < acl_cache[idx].acl->size; i++) { ! 527: if(acl_cache[idx].acl->tbl[i] != NULL ! 528: && strcmp(acl_cache[idx].acl->tbl[i], canon)) { ! 529: fputs(acl_cache[idx].acl->tbl[i], new); ! 530: putc('\n', new); ! 531: } ! 532: } ! 533: return(acl_commit(acl, new)); ! 534: } ! 535:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.