]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - server/acl_files.c
r4285@bucket (orig r275): kcr | 2008-01-21 15:03:56 -0500
[1ts-debian.git] / server / acl_files.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for maintaining Access Control Lists.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Id$
7  *
8  *      Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
9  *      For copying and distribution information, see the file
10  *      "mit-copyright.h". 
11  */
12
13 /* Define this if you really want the ACL-writing code included.  */
14
15 /*
16  * Stolen from lib/acl_files.c because acl_load needs to be externally
17  * declared and not statically declared.
18  */
19
20 #include <zephyr/mit-copyright.h>
21 #include "zserver.h"
22
23
24 #ifndef SABER
25 #ifndef lint
26 static const char rcsid_acl_files_c[] = "$Id$";
27 #endif /* lint */
28 #endif /* SABER */
29
30 /*** Routines for manipulating access control list files ***/
31
32 /* "aname.inst@realm" */
33 #define MAX_PRINCIPAL_SIZE  (ANAME_SZ + INST_SZ + REALM_SZ + 3)
34 #define INST_SEP '.'
35 #define REALM_SEP '@'
36
37 #define LINESIZE 2048           /* Maximum line length in an acl file */
38
39 #define NEW_FILE "%s.~NEWACL~"  /* Format for name of altered acl file */
40 #define WAIT_TIME 300           /* Maximum time allowed write acl file */
41
42 #define CACHED_ACLS 64          /* How many acls to cache */
43 #define ACL_LEN 256             /* Twice a reasonable acl length */
44
45 #define MAX(a,b) (((a)>(b))?(a):(b))
46 #define MIN(a,b) (((a)<(b))?(a):(b))
47
48 #define COR(a,b) ((a!=NULL)?(a):(b))
49
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,
56                                 char *canon)
57 {
58     char *end;
59     char *dot, *atsign;
60     int len;
61
62     dot = strchr(principal, INST_SEP);
63     atsign = strchr(principal, REALM_SEP);
64
65     /* Maybe we're done already */
66     if (dot != NULL && atsign != NULL) {
67         if (dot < atsign) {
68             /* It's for real */
69             /* Copy into canon */
70             strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
71             canon[MAX_PRINCIPAL_SIZE-1] = '\0';
72             return;
73         } else {
74             /* Nope, it's part of the realm */
75             dot = NULL;
76         }
77     }
78     
79     /* No such luck */
80     end = principal + strlen(principal);
81
82     /* Get the principal name */
83     len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
84     strncpy(canon, principal, len);
85     canon += len;
86
87     /* Add INST_SEP */
88     *canon++ = INST_SEP;
89
90     /* Get the instance, if it exists */
91     if (dot != NULL) {
92         ++dot;
93         len = MIN(INST_SZ, COR(atsign, end) - dot);
94         strncpy(canon, dot, len);
95         canon += len;
96     }
97
98     /* Add REALM_SEP */
99     *canon++ = REALM_SEP;
100
101     /* Get the realm, if it exists */
102     /* Otherwise, default to local realm */
103     if (atsign != NULL) {
104         ++atsign;
105         len = MIN(REALM_SZ, end - atsign);
106         strncpy(canon, atsign, len);
107         canon += len;
108         *canon++ = '\0';
109     } 
110 #ifdef HAVE_KRB4
111     else if (krb_get_lrealm(canon, 1) != KSUCCESS) {
112         strcpy(canon, KRB_REALM);
113     }
114 #endif
115 }
116
117 /* Eliminate all whitespace character in buf */
118 /* Modifies its argument */
119 static void
120 nuke_whitespace(char *buf)
121 {
122     char *pin, *pout;
123
124     for (pin = pout = buf; *pin != '\0'; pin++)
125         if (!isspace(*pin)) *pout++ = *pin;
126     *pout = '\0';               /* Terminate the string */
127 }
128
129 /* Hash table stuff */
130
131 struct hashtbl {
132     int size;                   /* Max number of entries */
133     int entries;                /* Actual number of entries */
134     char **tbl;                 /* Pointer to start of table */
135 };
136
137 /* Make an empty hash table of size s */
138 static struct hashtbl *
139 make_hash(int size)
140 {
141     struct hashtbl *h;
142
143     if (size < 1) size = 1;
144     h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
145     h->size = size;
146     h->entries = 0;
147     h->tbl = (char **) calloc(size, sizeof(char *));
148     return(h);
149 }
150
151 /* Destroy a hash table */
152 static void
153 destroy_hash(struct hashtbl *h)
154 {
155     int i;
156
157     for (i = 0; i < h->size; i++) {
158         if (h->tbl[i] != NULL) free(h->tbl[i]);
159     }
160     free(h->tbl);
161     free(h);
162 }
163
164 /* Compute hash value for a string */
165 static unsigned int
166 hashval(char *s)
167 {
168     unsigned hv;
169
170     for (hv = 0; *s != '\0'; s++) {
171         hv ^= ((hv << 3) ^ *s);
172     }
173     return(hv);
174 }
175
176 /* Add an element to a hash table */
177 static void
178 add_hash(struct hashtbl *h,
179          char *el)
180 {
181     unsigned hv;
182     char *s;
183     char **old;
184     int i;
185
186     /* Make space if it isn't there already */
187     if (h->entries + 1 > (h->size >> 1)) {
188         old = h->tbl;
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);
194                 h->tbl[hv] = old[i];
195             }
196         }
197         h->size = h->size << 1;
198         free(old);
199     }
200
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);
204     strcpy(s, el);
205     h->tbl[hv] = s;
206     h->entries++;
207 }
208
209 /* Returns nonzero if el is in h */
210 static int
211 check_hash(struct hashtbl *h,
212            char *el)
213 {
214     unsigned hv;
215
216     for (hv = hashval(el) % h->size; h->tbl[hv]; hv = (hv + 1) % h->size) {
217         if (!strcmp(h->tbl[hv], el)) {
218             return 1;
219         }
220     }
221     return 0;
222 }
223
224 struct acl {
225     char filename[LINESIZE];    /* Name of acl file */
226     struct hashtbl *acl;        /* Acl entries */
227 };
228
229 static struct acl acl_cache[CACHED_ACLS];
230
231 static int acl_cache_count = 0;
232 static int acl_cache_next = 0;
233
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)
238 {
239     int i;
240     FILE *f;
241     char buf[MAX_PRINCIPAL_SIZE];
242     char canon[MAX_PRINCIPAL_SIZE];
243
244     /* See if it's there already */
245     for (i = 0; i < acl_cache_count; i++) {
246         if (!strcmp(acl_cache[i].filename, name))
247             goto got_it;
248     }
249
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++;
254     } else {
255         /* No room, clean one out */
256         i = acl_cache_next;
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;
261         }
262     }
263
264     /* Set up the acl */
265     strcpy(acl_cache[i].filename, name);
266     /* Force reload */
267     acl_cache[i].acl = (struct hashtbl *) 0;
268
269   got_it:
270     /*
271      * See if we need to reload the ACL
272      */
273     if (acl_cache[i].acl == (struct hashtbl *) 0) {
274         /* Gotta reload */
275         if ((f = fopen(name, "r")) == NULL) {
276             syslog(LOG_ERR, "Error loading acl file %s: %m", name);
277             return -1;
278         }
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);
285         }
286         fclose(f);
287     }
288     return(i);
289 }
290
291 /*
292  * This destroys all cached ACL's so that new ones will be loaded in
293  * the next time they are requested.
294  */
295 void
296 acl_cache_reset(void)
297 {
298         int     i;
299         
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;
305             }
306         acl_cache_count = 0;
307         acl_cache_next = 0;
308     }
309
310
311 /* Returns nonzero if it can be determined that acl contains principal */
312 /* Principal is not canonicalized, and no wildcarding is done */
313 int
314 acl_exact_match(char *acl,
315                 char *principal)
316 {
317     int idx;
318
319     return((idx = acl_load(acl)) >= 0
320            && check_hash(acl_cache[idx].acl, principal));
321 }
322
323 /* Returns nonzero if it can be determined that acl contains principal */
324 /* Recognizes wildcards in acl. */
325 int
326 acl_check(char *acl,
327           char *principal)
328 {
329     char buf[MAX_PRINCIPAL_SIZE];
330     char canon[MAX_PRINCIPAL_SIZE];
331     char *instance, *realm;
332     int p, i, r;
333
334     /* Parse into principal, instance, and realm. */
335     acl_canonicalize_principal(principal, canon);
336     instance = (char *) strchr(canon, INST_SEP);
337     *instance++ = 0;
338     realm = (char *) strchr(instance, REALM_SEP);
339     *realm++ = 0;
340
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))
347                     return 1;
348             }
349         }
350     }
351        
352     return(0);
353 }
354
355 #ifdef notdef
356 /* Adds principal to acl */
357 /* Wildcards are interpreted literally */
358 int
359 acl_add(acl, principal)
360     char *acl;
361     char *principal;
362 {
363     int idx;
364     int i;
365     FILE *new;
366     char canon[MAX_PRINCIPAL_SIZE];
367
368     acl_canonicalize_principal(principal, canon);
369
370     if ((new = acl_lock_file(acl)) == NULL) return(-1);
371     if ((acl_exact_match(acl, canon))
372        || (idx = acl_load(acl)) < 0) {
373         acl_abort(acl, new);
374         return(-1);
375     }
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') {
381                 acl_abort(acl, new);
382                 return(-1);
383             }
384         }
385     }
386     fputs(canon, new);
387     putc('\n', new);
388     return(acl_commit(acl, new));
389 }
390
391 /* Removes principal from acl */
392 /* Wildcards are interpreted literally */
393 int
394 acl_delete(acl, principal)
395     char *acl;
396     char *principal;
397 {
398     int idx;
399     int i;
400     FILE *new;
401     char canon[MAX_PRINCIPAL_SIZE];
402
403     acl_canonicalize_principal(principal, canon);
404
405     if ((new = acl_lock_file(acl)) == NULL) return(-1);
406     if ((!acl_exact_match(acl, canon))
407        || (idx = acl_load(acl)) < 0) {
408         acl_abort(acl, new);
409         return(-1);
410     }
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);
416             putc('\n', new);
417         }
418     }
419     return(acl_commit(acl, new));
420 }
421 #endif /* notdef */