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
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. */
16 * Stolen from lib/acl_files.c because acl_load needs to be externally
17 * declared and not statically declared.
20 #include <zephyr/mit-copyright.h>
26 static const char rcsid_acl_files_c[] = "$Id$";
30 /*** Routines for manipulating access control list files ***/
32 /* "aname.inst@realm" */
33 #define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3)
37 #define LINESIZE 2048 /* Maximum line length in an acl file */
39 #define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */
40 #define WAIT_TIME 300 /* Maximum time allowed write acl file */
42 #define CACHED_ACLS 64 /* How many acls to cache */
43 #define ACL_LEN 256 /* Twice a reasonable acl length */
45 #define MAX(a,b) (((a)>(b))?(a):(b))
46 #define MIN(a,b) (((a)<(b))?(a):(b))
48 #define COR(a,b) ((a!=NULL)?(a):(b))
50 /* Canonicalize a principal name */
51 /* If instance is missing, it becomes "" */
52 /* If realm is missing, it becomes the local realm */
53 /* Canonicalized form is put in canon, which must be big enough to hold
54 MAX_PRINCIPAL_SIZE characters */
55 void acl_canonicalize_principal(char *principal,
62 dot = strchr(principal, INST_SEP);
63 atsign = strchr(principal, REALM_SEP);
65 /* Maybe we're done already */
66 if (dot != NULL && atsign != NULL) {
70 strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
71 canon[MAX_PRINCIPAL_SIZE-1] = '\0';
74 /* Nope, it's part of the realm */
80 end = principal + strlen(principal);
82 /* Get the principal name */
83 len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
84 strncpy(canon, principal, len);
90 /* Get the instance, if it exists */
93 len = MIN(INST_SZ, COR(atsign, end) - dot);
94 strncpy(canon, dot, len);
101 /* Get the realm, if it exists */
102 /* Otherwise, default to local realm */
103 if (atsign != NULL) {
105 len = MIN(REALM_SZ, end - atsign);
106 strncpy(canon, atsign, len);
111 else if (krb_get_lrealm(canon, 1) != KSUCCESS) {
112 strcpy(canon, KRB_REALM);
117 /* Eliminate all whitespace character in buf */
118 /* Modifies its argument */
120 nuke_whitespace(char *buf)
124 for (pin = pout = buf; *pin != '\0'; pin++)
125 if (!isspace(*pin)) *pout++ = *pin;
126 *pout = '\0'; /* Terminate the string */
129 /* Hash table stuff */
132 int size; /* Max number of entries */
133 int entries; /* Actual number of entries */
134 char **tbl; /* Pointer to start of table */
137 /* Make an empty hash table of size s */
138 static struct hashtbl *
143 if (size < 1) size = 1;
144 h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
147 h->tbl = (char **) calloc(size, sizeof(char *));
151 /* Destroy a hash table */
153 destroy_hash(struct hashtbl *h)
157 for (i = 0; i < h->size; i++) {
158 if (h->tbl[i] != NULL) free(h->tbl[i]);
164 /* Compute hash value for a string */
170 for (hv = 0; *s != '\0'; s++) {
171 hv ^= ((hv << 3) ^ *s);
176 /* Add an element to a hash table */
178 add_hash(struct hashtbl *h,
186 /* Make space if it isn't there already */
187 if (h->entries + 1 > (h->size >> 1)) {
189 h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
190 for (i = 0; i < h->size; i++) {
191 if (old[i] != NULL) {
192 hv = hashval(old[i]) % (h->size << 1);
193 while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
197 h->size = h->size << 1;
201 hv = hashval(el) % h->size;
202 while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
203 s = (char *) malloc(strlen(el)+1);
209 /* Returns nonzero if el is in h */
211 check_hash(struct hashtbl *h,
216 for (hv = hashval(el) % h->size; h->tbl[hv]; hv = (hv + 1) % h->size) {
217 if (!strcmp(h->tbl[hv], el)) {
225 char filename[LINESIZE]; /* Name of acl file */
226 struct hashtbl *acl; /* Acl entries */
229 static struct acl acl_cache[CACHED_ACLS];
231 static int acl_cache_count = 0;
232 static int acl_cache_next = 0;
234 /* Returns < 0 if unsuccessful in loading acl */
235 /* Returns index into acl_cache otherwise */
236 /* Note that if acl is already loaded, this is just a lookup */
237 int acl_load(char *name)
241 char buf[MAX_PRINCIPAL_SIZE];
242 char canon[MAX_PRINCIPAL_SIZE];
244 /* See if it's there already */
245 for (i = 0; i < acl_cache_count; i++) {
246 if (!strcmp(acl_cache[i].filename, name))
250 /* It isn't, load it in */
251 /* maybe there's still room */
252 if (acl_cache_count < CACHED_ACLS) {
253 i = acl_cache_count++;
255 /* No room, clean one out */
257 acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
258 if (acl_cache[i].acl) {
259 destroy_hash(acl_cache[i].acl);
260 acl_cache[i].acl = (struct hashtbl *) 0;
265 strcpy(acl_cache[i].filename, name);
267 acl_cache[i].acl = (struct hashtbl *) 0;
271 * See if we need to reload the ACL
273 if (acl_cache[i].acl == (struct hashtbl *) 0) {
275 if ((f = fopen(name, "r")) == NULL) {
276 syslog(LOG_ERR, "Error loading acl file %s: %m", name);
279 if (acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
280 acl_cache[i].acl = make_hash(ACL_LEN);
281 while(fgets(buf, sizeof(buf), f) != NULL) {
282 nuke_whitespace(buf);
283 acl_canonicalize_principal(buf, canon);
284 add_hash(acl_cache[i].acl, canon);
292 * This destroys all cached ACL's so that new ones will be loaded in
293 * the next time they are requested.
296 acl_cache_reset(void)
300 /* See if it's there already */
301 for (i = 0; i < acl_cache_count; i++)
302 if (acl_cache[i].acl) {
303 destroy_hash(acl_cache[i].acl);
304 acl_cache[i].acl = (struct hashtbl *) 0;
311 /* Returns nonzero if it can be determined that acl contains principal */
312 /* Principal is not canonicalized, and no wildcarding is done */
314 acl_exact_match(char *acl,
319 return((idx = acl_load(acl)) >= 0
320 && check_hash(acl_cache[idx].acl, principal));
323 /* Returns nonzero if it can be determined that acl contains principal */
324 /* Recognizes wildcards in acl. */
329 char buf[MAX_PRINCIPAL_SIZE];
330 char canon[MAX_PRINCIPAL_SIZE];
331 char *instance, *realm;
334 /* Parse into principal, instance, and realm. */
335 acl_canonicalize_principal(principal, canon);
336 instance = (char *) strchr(canon, INST_SEP);
338 realm = (char *) strchr(instance, REALM_SEP);
341 for (p = 0; p <= 1; p++) {
342 for (i = 0; i <= 1; i++) {
343 for (r = 0; r <= 1; r++) {
344 sprintf(buf, "%s%c%s%c%s", (p) ? canon : "*", INST_SEP,
345 (i) ? instance : "*", REALM_SEP, (r) ? realm : "*");
346 if (acl_exact_match(acl, buf))
356 /* Adds principal to acl */
357 /* Wildcards are interpreted literally */
359 acl_add(acl, principal)
366 char canon[MAX_PRINCIPAL_SIZE];
368 acl_canonicalize_principal(principal, canon);
370 if ((new = acl_lock_file(acl)) == NULL) return(-1);
371 if ((acl_exact_match(acl, canon))
372 || (idx = acl_load(acl)) < 0) {
376 /* It isn't there yet, copy the file and put it in */
377 for (i = 0; i < acl_cache[idx].acl->size; i++) {
378 if (acl_cache[idx].acl->tbl[i] != NULL) {
379 if (fputs(acl_cache[idx].acl->tbl[i], new) == NULL
380 || putc('\n', new) != '\n') {
388 return(acl_commit(acl, new));
391 /* Removes principal from acl */
392 /* Wildcards are interpreted literally */
394 acl_delete(acl, principal)
401 char canon[MAX_PRINCIPAL_SIZE];
403 acl_canonicalize_principal(principal, canon);
405 if ((new = acl_lock_file(acl)) == NULL) return(-1);
406 if ((!acl_exact_match(acl, canon))
407 || (idx = acl_load(acl)) < 0) {
411 /* It isn't there yet, copy the file and put it in */
412 for (i = 0; i < acl_cache[idx].acl->size; i++) {
413 if (acl_cache[idx].acl->tbl[i] != NULL
414 && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
415 fputs(acl_cache[idx].acl->tbl[i], new);
419 return(acl_commit(acl, new));