|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.