1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains functions for maintaining Access Control Lists.
4 * Created by: John T. Kohl
6 * $Id: acl_files.c,v 3.3 1999/01/22 23:19:38 ghudson Exp $
8 * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file
13 /* Define this if you really want the ACL-writing code included. */
14 /* #define WRITE_ACL */
17 * Stolen from lib/acl_files.c because acl_load needs to be externally
18 * declared and not statically declared.
21 #include <zephyr/mit-copyright.h>
27 static const char rcsid_acl_files_c[] = "$Id: acl_files.c,v 3.3 1999/01/22 23:19:38 ghudson Exp $";
31 /*** Routines for manipulating access control list files ***/
33 /* "aname.inst@realm" */
34 #define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3)
38 #define LINESIZE 2048 /* Maximum line length in an acl file */
40 #define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */
41 #define WAIT_TIME 300 /* Maximum time allowed write acl file */
43 #define CACHED_ACLS 64 /* How many acls to cache */
44 #define ACL_LEN 256 /* Twice a reasonable acl length */
46 #define MAX(a,b) (((a)>(b))?(a):(b))
47 #define MIN(a,b) (((a)<(b))?(a):(b))
49 #define COR(a,b) ((a!=NULL)?(a):(b))
55 /* Canonicalize a principal name */
56 /* If instance is missing, it becomes "" */
57 /* If realm is missing, it becomes the local realm */
58 /* Canonicalized form is put in canon, which must be big enough to hold
59 MAX_PRINCIPAL_SIZE characters */
60 void acl_canonicalize_principal(principal, canon)
68 dot = strchr(principal, INST_SEP);
69 atsign = strchr(principal, REALM_SEP);
71 /* Maybe we're done already */
72 if (dot != NULL && atsign != NULL) {
76 strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
77 canon[MAX_PRINCIPAL_SIZE-1] = '\0';
80 /* Nope, it's part of the realm */
86 end = principal + strlen(principal);
88 /* Get the principal name */
89 len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
90 strncpy(canon, principal, len);
96 /* Get the instance, if it exists */
99 len = MIN(INST_SZ, COR(atsign, end) - dot);
100 strncpy(canon, dot, len);
105 *canon++ = REALM_SEP;
107 /* Get the realm, if it exists */
108 /* Otherwise, default to local realm */
109 if (atsign != NULL) {
111 len = MIN(REALM_SZ, end - atsign);
112 strncpy(canon, atsign, len);
117 else if (krb_get_lrealm(canon, 1) != KSUCCESS) {
118 strcpy(canon, KRB_REALM);
124 /* Get a lock to modify acl_file */
125 /* Return new FILE pointer */
126 /* or NULL if file cannot be modified */
127 /* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */
128 static FILE *acl_lock_file(acl_file)
137 if (stat(acl_file, &s) < 0) return(NULL);
139 sprintf(new, NEW_FILE, acl_file);
141 /* Open the new file */
142 if ((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
143 if (errno == EEXIST) {
144 /* Maybe somebody got here already, maybe it's just old */
145 if (stat(new, &s) < 0) return(NULL);
146 if (time(0) - s.st_ctime > WAIT_TIME) {
147 /* File is stale, kill it */
151 /* Wait and try again */
156 /* Some other error, we lose */
161 /* If we got to here, the lock file is ours and ok */
162 /* Reopen it under stdio */
163 if ((nf = fdopen(nfd, "w")) == NULL) {
171 /* Commit changes to acl_file written onto FILE *f */
172 /* Returns zero if successful */
173 /* Returns > 0 if lock was broken */
174 /* Returns < 0 if some other error occurs */
176 static int acl_commit(acl_file, f)
185 sprintf(new, NEW_FILE, acl_file);
187 || fstat(fileno(f), &s) < 0
188 || s.st_nlink == 0) {
189 acl_abort(acl_file, f);
193 ret = rename(new, acl_file);
201 /* Abort changes to acl_file written onto FILE *f */
202 /* Returns 0 if successful, < 0 otherwise */
204 static int acl_abort(acl_file, f)
213 /* make sure we aren't nuking someone else's file */
214 if (fstat(fileno(f), &s) < 0 || s.st_nlink == 0) {
218 sprintf(new, NEW_FILE, acl_file);
228 /* Initialize an acl_file */
229 /* Creates the file with permissions perm if it does not exist */
230 /* Erases it if it does */
231 /* Returns return value of acl_commit */
233 acl_initialize(acl_file, perm)
240 /* Check if the file exists already */
241 if ((new = acl_lock_file(acl_file)) != NULL) {
242 return(acl_commit(acl_file, new));
244 /* File must be readable and writable by owner */
245 if ((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) {
256 /* Eliminate all whitespace character in buf */
257 /* Modifies its argument */
258 static void nuke_whitespace(buf)
263 for (pin = pout = buf; *pin != '\0'; pin++)
264 if (!isspace(*pin)) *pout++ = *pin;
265 *pout = '\0'; /* Terminate the string */
268 /* Hash table stuff */
271 int size; /* Max number of entries */
272 int entries; /* Actual number of entries */
273 char **tbl; /* Pointer to start of table */
276 /* Make an empty hash table of size s */
277 static struct hashtbl *make_hash(size)
282 if (size < 1) size = 1;
283 h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
286 h->tbl = (char **) calloc(size, sizeof(char *));
290 /* Destroy a hash table */
297 for (i = 0; i < h->size; i++) {
298 if (h->tbl[i] != NULL) free(h->tbl[i]);
304 /* Compute hash value for a string */
311 for (hv = 0; *s != '\0'; s++) {
312 hv ^= ((hv << 3) ^ *s);
317 /* Add an element to a hash table */
318 static void add_hash(h, el)
328 fprintf (stderr, "adding %s to acl hash %08X\n", el, h);
330 /* Make space if it isn't there already */
331 if (h->entries + 1 > (h->size >> 1)) {
333 h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
334 for (i = 0; i < h->size; i++) {
335 if (old[i] != NULL) {
336 hv = hashval(old[i]) % (h->size << 1);
337 while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
341 h->size = h->size << 1;
345 hv = hashval(el) % h->size;
346 while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
347 s = (char *) malloc(strlen(el)+1);
353 /* Returns nonzero if el is in h */
362 fprintf (stderr, "looking for %s in acl %08X\n", el, h);
364 for (hv = hashval(el) % h->size; h->tbl[hv]; hv = (hv + 1) % h->size) {
366 fprintf (stderr, "\tstrcmp (%s,...)\n", h->tbl[hv]);
368 if (!strcmp(h->tbl[hv], el)) {
370 fprintf (stderr, "success!\n");
376 fprintf (stderr, "failure\n");
382 char filename[LINESIZE]; /* Name of acl file */
383 struct hashtbl *acl; /* Acl entries */
386 static struct acl acl_cache[CACHED_ACLS];
388 static int acl_cache_count = 0;
389 static int acl_cache_next = 0;
391 /* Returns < 0 if unsuccessful in loading acl */
392 /* Returns index into acl_cache otherwise */
393 /* Note that if acl is already loaded, this is just a lookup */
399 char buf[MAX_PRINCIPAL_SIZE];
400 char canon[MAX_PRINCIPAL_SIZE];
402 /* See if it's there already */
403 for (i = 0; i < acl_cache_count; i++) {
404 if (!strcmp(acl_cache[i].filename, name))
408 /* It isn't, load it in */
409 /* maybe there's still room */
410 if (acl_cache_count < CACHED_ACLS) {
411 i = acl_cache_count++;
413 /* No room, clean one out */
415 acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
416 if (acl_cache[i].acl) {
417 destroy_hash(acl_cache[i].acl);
418 acl_cache[i].acl = (struct hashtbl *) 0;
423 strcpy(acl_cache[i].filename, name);
425 acl_cache[i].acl = (struct hashtbl *) 0;
429 * See if we need to reload the ACL
431 if (acl_cache[i].acl == (struct hashtbl *) 0) {
434 fprintf (stderr, "attempting to load %s\n", name);
436 if ((f = fopen(name, "r")) == NULL) {
442 if (acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
443 acl_cache[i].acl = make_hash(ACL_LEN);
444 while(fgets(buf, sizeof(buf), f) != NULL) {
445 nuke_whitespace(buf);
446 acl_canonicalize_principal(buf, canon);
447 add_hash(acl_cache[i].acl, canon);
455 * This destroys all cached ACL's so that new ones will be loaded in
456 * the next time they are requested.
463 /* See if it's there already */
464 for (i = 0; i < acl_cache_count; i++)
465 if (acl_cache[i].acl) {
466 destroy_hash(acl_cache[i].acl);
467 acl_cache[i].acl = (struct hashtbl *) 0;
474 /* Returns nonzero if it can be determined that acl contains principal */
475 /* Principal is not canonicalized, and no wildcarding is done */
476 acl_exact_match(acl, principal)
483 fprintf (stderr, "checking for %s in %s\n", principal, acl);
485 return((idx = acl_load(acl)) >= 0
486 && check_hash(acl_cache[idx].acl, principal));
489 /* Returns nonzero if it can be determined that acl contains principal */
490 /* Recognizes wildcards in acl. */
492 acl_check(acl, principal)
496 char buf[MAX_PRINCIPAL_SIZE];
497 char canon[MAX_PRINCIPAL_SIZE];
498 char *instance, *realm;
501 /* Parse into principal, instance, and realm. */
502 acl_canonicalize_principal(principal, canon);
503 instance = (char *) strchr(canon, INST_SEP);
505 realm = (char *) strchr(instance, REALM_SEP);
508 for (p = 0; p <= 1; p++) {
509 for (i = 0; i <= 1; i++) {
510 for (r = 0; r <= 1; r++) {
511 sprintf(buf, "%s%c%s%c%s", (p) ? canon : "*", INST_SEP,
512 (i) ? instance : "*", REALM_SEP, (r) ? realm : "*");
513 if (acl_exact_match(acl, buf))
523 /* Adds principal to acl */
524 /* Wildcards are interpreted literally */
526 acl_add(acl, principal)
533 char canon[MAX_PRINCIPAL_SIZE];
535 acl_canonicalize_principal(principal, canon);
537 if ((new = acl_lock_file(acl)) == NULL) return(-1);
538 if ((acl_exact_match(acl, canon))
539 || (idx = acl_load(acl)) < 0) {
543 /* It isn't there yet, copy the file and put it in */
544 for (i = 0; i < acl_cache[idx].acl->size; i++) {
545 if (acl_cache[idx].acl->tbl[i] != NULL) {
546 if (fputs(acl_cache[idx].acl->tbl[i], new) == NULL
547 || putc('\n', new) != '\n') {
555 return(acl_commit(acl, new));
558 /* Removes principal from acl */
559 /* Wildcards are interpreted literally */
561 acl_delete(acl, principal)
568 char canon[MAX_PRINCIPAL_SIZE];
570 acl_canonicalize_principal(principal, canon);
572 if ((new = acl_lock_file(acl)) == NULL) return(-1);
573 if ((!acl_exact_match(acl, canon))
574 || (idx = acl_load(acl)) < 0) {
578 /* It isn't there yet, copy the file and put it in */
579 for (i = 0; i < acl_cache[idx].acl->size; i++) {
580 if (acl_cache[idx].acl->tbl[i] != NULL
581 && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
582 fputs(acl_cache[idx].acl->tbl[i], new);
586 return(acl_commit(acl, new));