Annotation of 43BSDReno/kerberosIV/acl/acl_files.c, revision 1.1

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: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.