Annotation of 43BSDReno/kerberosIV/acl/acl_files.c, revision 1.1.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.