home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / kerberosIV / acl / acl_files.c next >
Encoding:
C/C++ Source or Header  |  1991-02-25  |  12.7 KB  |  538 lines

  1. /*
  2.  *    $Source: /usr/src/kerberosIV/acl/RCS/acl_files.c,v $
  3.  *    $Author: bostic $
  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.6 91/02/25 15:11:48 bostic 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. static int acl_abort();
  169.  
  170. /* Commit changes to acl_file written onto FILE *f */
  171. /* Returns zero if successful */
  172. /* Returns > 0 if lock was broken */
  173. /* Returns < 0 if some other error occurs */
  174. /* Closes f */
  175. static int acl_commit(acl_file, f)
  176. char *acl_file;
  177. FILE *f;     
  178. {
  179.     char new[LINESIZE];
  180.     int ret;
  181.     struct stat s;
  182.  
  183.     sprintf(new, NEW_FILE, acl_file);
  184.     if(fflush(f) < 0
  185.        || fstat(fileno(f), &s) < 0
  186.        || s.st_nlink == 0) {
  187.     acl_abort(acl_file, f);
  188.     return(-1);
  189.     }
  190.  
  191.     ret = rename(new, acl_file);
  192.     fclose(f);
  193.     return(ret);
  194. }
  195.  
  196. /* Abort changes to acl_file written onto FILE *f */
  197. /* Returns 0 if successful, < 0 otherwise */
  198. /* Closes f */
  199. static int acl_abort(acl_file, f)
  200. char *acl_file;
  201. FILE *f;     
  202. {
  203.     char new[LINESIZE];
  204.     int ret;
  205.     struct stat s;
  206.  
  207.     /* make sure we aren't nuking someone else's file */
  208.     if(fstat(fileno(f), &s) < 0
  209.        || s.st_nlink == 0) {
  210.        fclose(f);
  211.        return(-1);
  212.        } else {
  213.        sprintf(new, NEW_FILE, acl_file);
  214.        ret = unlink(new);
  215.        fclose(f);
  216.        return(ret);
  217.        }
  218. }
  219.  
  220. /* Initialize an acl_file */
  221. /* Creates the file with permissions perm if it does not exist */
  222. /* Erases it if it does */
  223. /* Returns return value of acl_commit */
  224. int acl_initialize(acl_file, perm)
  225. char *acl_file;
  226. int perm;
  227. {
  228.     FILE *new;
  229.     int fd;
  230.  
  231.     /* Check if the file exists already */
  232.     if((new = acl_lock_file(acl_file)) != NULL) {
  233.     return(acl_commit(acl_file, new));
  234.     } else {
  235.     /* File must be readable and writable by owner */
  236.     if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) {
  237.         return(-1);
  238.     } else {
  239.         close(fd);
  240.         return(0);
  241.     }
  242.     }
  243. }
  244.  
  245. /* Eliminate all whitespace character in buf */
  246. /* Modifies its argument */
  247. static nuke_whitespace(buf)
  248. char *buf;
  249. {
  250.     register char *pin, *pout;
  251.  
  252.     for(pin = pout = buf; *pin != '\0'; pin++)
  253.     if(!isspace(*pin)) *pout++ = *pin;
  254.     *pout = '\0';        /* Terminate the string */
  255. }
  256.  
  257. /* Hash table stuff */
  258.  
  259. struct hashtbl {
  260.     int size;            /* Max number of entries */
  261.     int entries;        /* Actual number of entries */
  262.     char **tbl;            /* Pointer to start of table */
  263. };
  264.  
  265. /* Make an empty hash table of size s */
  266. static struct hashtbl *make_hash(size)
  267. int size;
  268. {
  269.     struct hashtbl *h;
  270.  
  271.     if(size < 1) size = 1;
  272.     h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
  273.     h->size = size;
  274.     h->entries = 0;
  275.     h->tbl = (char **) calloc(size, sizeof(char *));
  276.     return(h);
  277. }
  278.  
  279. /* Destroy a hash table */
  280. static destroy_hash(h)
  281. struct hashtbl *h;
  282. {
  283.     int i;
  284.  
  285.     for(i = 0; i < h->size; i++) {
  286.     if(h->tbl[i] != NULL) free(h->tbl[i]);
  287.     }
  288.     free(h->tbl);
  289.     free(h);
  290. }
  291.  
  292. /* Compute hash value for a string */
  293. static unsigned hashval(s)
  294. register char *s;
  295. {
  296.     register unsigned hv;
  297.  
  298.     for(hv = 0; *s != '\0'; s++) {
  299.     hv ^= ((hv << 3) ^ *s);
  300.     }
  301.     return(hv);
  302. }
  303.  
  304. /* Add an element to a hash table */
  305. static add_hash(h, el)
  306. struct hashtbl *h;
  307. char *el;
  308. {
  309.     unsigned hv;
  310.     char *s;
  311.     char **old;
  312.     int i;
  313.  
  314.     /* Make space if it isn't there already */
  315.     if(h->entries + 1 > (h->size >> 1)) {
  316.     old = h->tbl;
  317.     h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
  318.     for(i = 0; i < h->size; i++) {
  319.         if(old[i] != NULL) {
  320.         hv = hashval(old[i]) % (h->size << 1);
  321.         while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
  322.         h->tbl[hv] = old[i];
  323.         }
  324.     }
  325.     h->size = h->size << 1;
  326.     free(old);
  327.     }
  328.  
  329.     hv = hashval(el) % h->size;
  330.     while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
  331.     s = malloc(strlen(el)+1);
  332.     strcpy(s, el);
  333.     h->tbl[hv] = s;
  334.     h->entries++;
  335. }
  336.  
  337. /* Returns nonzero if el is in h */
  338. static check_hash(h, el)
  339. struct hashtbl *h;
  340. char *el;
  341. {
  342.     unsigned hv;
  343.  
  344.     for(hv = hashval(el) % h->size;
  345.     h->tbl[hv] != NULL;
  346.     hv = (hv + 1) % h->size) {
  347.         if(!strcmp(h->tbl[hv], el)) return(1);
  348.     }
  349.     return(0);
  350. }
  351.  
  352. struct acl {
  353.     char filename[LINESIZE];    /* Name of acl file */
  354.     int fd;            /* File descriptor for acl file */
  355.     struct stat status;        /* File status at last read */
  356.     struct hashtbl *acl;    /* Acl entries */
  357. };
  358.  
  359. static struct acl acl_cache[CACHED_ACLS];
  360.  
  361. static int acl_cache_count = 0;
  362. static int acl_cache_next = 0;
  363.  
  364. /* Returns < 0 if unsuccessful in loading acl */
  365. /* Returns index into acl_cache otherwise */
  366. /* Note that if acl is already loaded, this is just a lookup */
  367. static int acl_load(name)
  368. char *name;
  369. {
  370.     int i;
  371.     FILE *f;
  372.     struct stat s;
  373.     char buf[MAX_PRINCIPAL_SIZE];
  374.     char canon[MAX_PRINCIPAL_SIZE];
  375.  
  376.     /* See if it's there already */
  377.     for(i = 0; i < acl_cache_count; i++) {
  378.     if(!strcmp(acl_cache[i].filename, name)
  379.        && acl_cache[i].fd >= 0) goto got_it;
  380.     }
  381.  
  382.     /* It isn't, load it in */
  383.     /* maybe there's still room */
  384.     if(acl_cache_count < CACHED_ACLS) {
  385.     i = acl_cache_count++;
  386.     } else {
  387.     /* No room, clean one out */
  388.     i = acl_cache_next;
  389.     acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
  390.     close(acl_cache[i].fd);
  391.     if(acl_cache[i].acl) {
  392.         destroy_hash(acl_cache[i].acl);
  393.         acl_cache[i].acl = (struct hashtbl *) 0;
  394.     }
  395.     }
  396.  
  397.     /* Set up the acl */
  398.     strcpy(acl_cache[i].filename, name);
  399.     if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
  400.     /* Force reload */
  401.     acl_cache[i].acl = (struct hashtbl *) 0;
  402.  
  403.  got_it:
  404.     /*
  405.      * See if the stat matches
  406.      *
  407.      * Use stat(), not fstat(), as the file may have been re-created by
  408.      * acl_add or acl_delete.  If this happens, the old inode will have
  409.      * no changes in the mod-time and the following test will fail.
  410.      */
  411.     if(stat(acl_cache[i].filename, &s) < 0) return(-1);
  412.     if(acl_cache[i].acl == (struct hashtbl *) 0
  413.        || s.st_nlink != acl_cache[i].status.st_nlink
  414.        || s.st_mtime != acl_cache[i].status.st_mtime
  415.        || s.st_ctime != acl_cache[i].status.st_ctime) {
  416.        /* Gotta reload */
  417.        if(acl_cache[i].fd >= 0) close(acl_cache[i].fd);
  418.        if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
  419.        if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1);
  420.        if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
  421.        acl_cache[i].acl = make_hash(ACL_LEN);
  422.        while(fgets(buf, sizeof(buf), f) != NULL) {
  423.            nuke_whitespace(buf);
  424.            acl_canonicalize_principal(buf, canon);
  425.            add_hash(acl_cache[i].acl, canon);
  426.        }
  427.        fclose(f);
  428.        acl_cache[i].status = s;
  429.        }
  430.     return(i);
  431. }
  432.  
  433. /* Returns nonzero if it can be determined that acl contains principal */
  434. /* Principal is not canonicalized, and no wildcarding is done */
  435. acl_exact_match(acl, principal)
  436. char *acl;
  437. char *principal;
  438. {
  439.     int idx;
  440.  
  441.     return((idx = acl_load(acl)) >= 0
  442.        && check_hash(acl_cache[idx].acl, principal));
  443. }
  444.  
  445. /* Returns nonzero if it can be determined that acl contains principal */
  446. /* Recognizes wildcards in acl of the form
  447.    name.*@realm, *.*@realm, and *.*@* */
  448. acl_check(acl, principal)
  449. char *acl;
  450. char *principal;
  451. {
  452.     char buf[MAX_PRINCIPAL_SIZE];
  453.     char canon[MAX_PRINCIPAL_SIZE];
  454.     char *realm;
  455.  
  456.     acl_canonicalize_principal(principal, canon);
  457.  
  458.     /* Is it there? */
  459.     if(acl_exact_match(acl, canon)) return(1);
  460.  
  461.     /* Try the wildcards */
  462.     realm = index(canon, REALM_SEP);
  463.     *index(canon, INST_SEP) = '\0';    /* Chuck the instance */
  464.  
  465.     sprintf(buf, "%s.*%s", canon, realm);
  466.     if(acl_exact_match(acl, buf)) return(1);
  467.  
  468.     sprintf(buf, "*.*%s", realm);
  469.     if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1);
  470.        
  471.     return(0);
  472. }
  473.  
  474. /* Adds principal to acl */
  475. /* Wildcards are interpreted literally */
  476. acl_add(acl, principal)
  477. char *acl;
  478. char *principal;
  479. {
  480.     int idx;
  481.     int i;
  482.     FILE *new;
  483.     char canon[MAX_PRINCIPAL_SIZE];
  484.  
  485.     acl_canonicalize_principal(principal, canon);
  486.  
  487.     if((new = acl_lock_file(acl)) == NULL) return(-1);
  488.     if((acl_exact_match(acl, canon))
  489.        || (idx = acl_load(acl)) < 0) {
  490.        acl_abort(acl, new);
  491.        return(-1);
  492.        }
  493.     /* It isn't there yet, copy the file and put it in */
  494.     for(i = 0; i < acl_cache[idx].acl->size; i++) {
  495.     if(acl_cache[idx].acl->tbl[i] != NULL) {
  496.         if(fputs(acl_cache[idx].acl->tbl[i], new) == NULL
  497.            || putc('\n', new) != '\n') {
  498.            acl_abort(acl, new);
  499.            return(-1);
  500.            }
  501.     }
  502.     }
  503.     fputs(canon, new);
  504.     putc('\n', new);
  505.     return(acl_commit(acl, new));
  506. }
  507.  
  508. /* Removes principal from acl */
  509. /* Wildcards are interpreted literally */
  510. acl_delete(acl, principal)
  511. char *acl;
  512. char *principal;
  513. {
  514.     int idx;
  515.     int i;
  516.     FILE *new;
  517.     char canon[MAX_PRINCIPAL_SIZE];
  518.  
  519.     acl_canonicalize_principal(principal, canon);
  520.  
  521.     if((new = acl_lock_file(acl)) == NULL) return(-1);
  522.     if((!acl_exact_match(acl, canon))
  523.        || (idx = acl_load(acl)) < 0) {
  524.        acl_abort(acl, new);
  525.        return(-1);
  526.        }
  527.     /* It isn't there yet, copy the file and put it in */
  528.     for(i = 0; i < acl_cache[idx].acl->size; i++) {
  529.     if(acl_cache[idx].acl->tbl[i] != NULL
  530.        && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
  531.            fputs(acl_cache[idx].acl->tbl[i], new);
  532.            putc('\n', new);
  533.     }
  534.     }
  535.     return(acl_commit(acl, new));
  536. }
  537.  
  538.