]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/acl_files.c
need automake as a build-dep, even though we don't use most of it
[1ts-debian.git] / zephyr / 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 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
23 /* this needs to be rethought for a world without krb4 */
24
25 #ifndef SABER
26 #ifndef lint
27 static const char rcsid_acl_files_c[] = "$Id$";
28 #endif /* lint */
29 #endif /* SABER */
30
31 /*** Routines for manipulating access control list files ***/
32
33 #ifndef ANAME_SZ
34 #define ANAME_SZ 40
35 #define INST_SZ 40
36 #endif /* XXX */
37
38 /* "aname.inst@realm" */
39 #ifndef MAX_PRINCIPAL_SIZE
40 #define MAX_PRINCIPAL_SIZE  (ANAME_SZ + INST_SZ + REALM_SZ + 3)
41 #endif
42 #define INST_SEP '.'
43 #define REALM_SEP '@'
44
45 #define LINESIZE 2048           /* Maximum line length in an acl file */
46
47 #define NEW_FILE "%s.~NEWACL~"  /* Format for name of altered acl file */
48 #define WAIT_TIME 300           /* Maximum time allowed write acl file */
49
50 #define CACHED_ACLS 64          /* How many acls to cache */
51 #define ACL_LEN 256             /* Twice a reasonable acl length */
52
53 #define MAX(a,b) (((a)>(b))?(a):(b))
54 #define MIN(a,b) (((a)<(b))?(a):(b))
55
56 #define COR(a,b) ((a!=NULL)?(a):(b))
57
58 /* Canonicalize a principal name */
59 /* If instance is missing, it becomes "" */
60 /* If realm is missing, it becomes the local realm */
61 /* Canonicalized form is put in canon, which must be big enough to hold
62    MAX_PRINCIPAL_SIZE characters */
63 void acl_canonicalize_principal(char *principal,
64                                 char *canon)
65 {
66     char *end;
67     char *dot, *atsign;
68     int len;
69
70     dot = strchr(principal, INST_SEP);
71     atsign = strchr(principal, REALM_SEP);
72
73     /* Maybe we're done already */
74     if (dot != NULL && atsign != NULL) {
75         if (dot < atsign) {
76             /* It's for real */
77             /* Copy into canon */
78             strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
79             canon[MAX_PRINCIPAL_SIZE-1] = '\0';
80             return;
81         } else {
82             /* Nope, it's part of the realm */
83             dot = NULL;
84         }
85     }
86     
87     /* No such luck */
88     end = principal + strlen(principal);
89
90     /* Get the principal name */
91     len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
92     strncpy(canon, principal, len);
93     canon += len;
94
95     /* Add INST_SEP */
96     *canon++ = INST_SEP;
97
98     /* Get the instance, if it exists */
99     if (dot != NULL) {
100         ++dot;
101         len = MIN(INST_SZ, COR(atsign, end) - dot);
102         strncpy(canon, dot, len);
103         canon += len;
104     }
105
106     /* Add REALM_SEP */
107     *canon++ = REALM_SEP;
108
109     /* Get the realm, if it exists */
110     /* Otherwise, default to local realm */
111     if (atsign != NULL) {
112         ++atsign;
113         len = MIN(REALM_SZ, end - atsign);
114         strncpy(canon, atsign, len);
115         canon += len;
116         *canon++ = '\0';
117     } 
118 #ifdef HAVE_KRB4
119     else if (krb_get_lrealm(canon, 1) != KSUCCESS) {
120         strcpy(canon, KRB_REALM);
121     }
122 #endif
123 }
124
125 /* Eliminate all whitespace character in buf */
126 /* Modifies its argument */
127 static void
128 nuke_whitespace(char *buf)
129 {
130     char *pin, *pout;
131
132     for (pin = pout = buf; *pin != '\0'; pin++)
133         if (!isspace(*pin)) *pout++ = *pin;
134     *pout = '\0';               /* Terminate the string */
135 }
136
137 /* Hash table stuff */
138
139 struct hashtbl {
140     int size;                   /* Max number of entries */
141     int entries;                /* Actual number of entries */
142     char **tbl;                 /* Pointer to start of table */
143 };
144
145 /* Make an empty hash table of size s */
146 static struct hashtbl *
147 make_hash(int size)
148 {
149     struct hashtbl *h;
150
151     if (size < 1) size = 1;
152     h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
153     h->size = size;
154     h->entries = 0;
155     h->tbl = (char **) calloc(size, sizeof(char *));
156     return(h);
157 }
158
159 /* Destroy a hash table */
160 static void
161 destroy_hash(struct hashtbl *h)
162 {
163     int i;
164
165     for (i = 0; i < h->size; i++) {
166         if (h->tbl[i] != NULL) free(h->tbl[i]);
167     }
168     free(h->tbl);
169     free(h);
170 }
171
172 /* Compute hash value for a string */
173 static unsigned int
174 hashval(char *s)
175 {
176     unsigned hv;
177
178     for (hv = 0; *s != '\0'; s++) {
179         hv ^= ((hv << 3) ^ *s);
180     }
181     return(hv);
182 }
183
184 /* Add an element to a hash table */
185 static void
186 add_hash(struct hashtbl *h,
187          char *el)
188 {
189     unsigned hv;
190     char *s;
191     char **old;
192     int i;
193
194     /* Make space if it isn't there already */
195     if (h->entries + 1 > (h->size >> 1)) {
196         old = h->tbl;
197         h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
198         for (i = 0; i < h->size; i++) {
199             if (old[i] != NULL) {
200                 hv = hashval(old[i]) % (h->size << 1);
201                 while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
202                 h->tbl[hv] = old[i];
203             }
204         }
205         h->size = h->size << 1;
206         free(old);
207     }
208
209     hv = hashval(el) % h->size;
210     while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
211     s = (char *) malloc(strlen(el)+1);
212     strcpy(s, el);
213     h->tbl[hv] = s;
214     h->entries++;
215 }
216
217 /* Returns nonzero if el is in h */
218 static int
219 check_hash(struct hashtbl *h,
220            char *el)
221 {
222     unsigned hv;
223
224     for (hv = hashval(el) % h->size; h->tbl[hv]; hv = (hv + 1) % h->size) {
225         if (!strcmp(h->tbl[hv], el)) {
226             return 1;
227         }
228     }
229     return 0;
230 }
231
232 struct acl {
233     char filename[LINESIZE];    /* Name of acl file */
234     struct hashtbl *acl;        /* Acl entries */
235 };
236
237 static struct acl acl_cache[CACHED_ACLS];
238
239 static int acl_cache_count = 0;
240 static int acl_cache_next = 0;
241
242 /* Returns < 0 if unsuccessful in loading acl */
243 /* Returns index into acl_cache otherwise */
244 /* Note that if acl is already loaded, this is just a lookup */
245 int acl_load(char *name)
246 {
247     int i;
248     FILE *f;
249     char buf[MAX_PRINCIPAL_SIZE];
250     char canon[MAX_PRINCIPAL_SIZE];
251
252     /* See if it's there already */
253     for (i = 0; i < acl_cache_count; i++) {
254         if (!strcmp(acl_cache[i].filename, name))
255             goto got_it;
256     }
257
258     /* It isn't, load it in */
259     /* maybe there's still room */
260     if (acl_cache_count < CACHED_ACLS) {
261         i = acl_cache_count++;
262     } else {
263         /* No room, clean one out */
264         i = acl_cache_next;
265         acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
266         if (acl_cache[i].acl) {
267             destroy_hash(acl_cache[i].acl);
268             acl_cache[i].acl = (struct hashtbl *) 0;
269         }
270     }
271
272     /* Set up the acl */
273     strcpy(acl_cache[i].filename, name);
274     /* Force reload */
275     acl_cache[i].acl = (struct hashtbl *) 0;
276
277   got_it:
278     /*
279      * See if we need to reload the ACL
280      */
281     if (acl_cache[i].acl == (struct hashtbl *) 0) {
282         /* Gotta reload */
283         if ((f = fopen(name, "r")) == NULL) {
284             syslog(LOG_ERR, "Error loading acl file %s: %m", name);
285             return -1;
286         }
287         if (acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
288         acl_cache[i].acl = make_hash(ACL_LEN);
289         while(fgets(buf, sizeof(buf), f) != NULL) {
290             nuke_whitespace(buf);
291             acl_canonicalize_principal(buf, canon);
292             add_hash(acl_cache[i].acl, canon);
293         }
294         fclose(f);
295     }
296     return(i);
297 }
298
299 /*
300  * This destroys all cached ACL's so that new ones will be loaded in
301  * the next time they are requested.
302  */
303 void
304 acl_cache_reset(void)
305 {
306         int     i;
307         
308         /* See if it's there already */
309         for (i = 0; i < acl_cache_count; i++)
310             if (acl_cache[i].acl) {
311                 destroy_hash(acl_cache[i].acl);
312                 acl_cache[i].acl = (struct hashtbl *) 0;
313             }
314         acl_cache_count = 0;
315         acl_cache_next = 0;
316     }
317
318
319 /* Returns nonzero if it can be determined that acl contains principal */
320 /* Principal is not canonicalized, and no wildcarding is done */
321 int
322 acl_exact_match(char *acl,
323                 char *principal)
324 {
325     int idx;
326
327     return((idx = acl_load(acl)) >= 0
328            && check_hash(acl_cache[idx].acl, principal));
329 }
330
331 /* Returns nonzero if it can be determined that acl contains principal */
332 /* Recognizes wildcards in acl. */
333 int
334 acl_check(char *acl,
335           char *principal)
336 {
337     char buf[MAX_PRINCIPAL_SIZE];
338     char canon[MAX_PRINCIPAL_SIZE];
339     char *instance, *realm;
340     int p, i, r;
341
342     /* Parse into principal, instance, and realm. */
343     acl_canonicalize_principal(principal, canon);
344     instance = (char *) strchr(canon, INST_SEP);
345     *instance++ = 0;
346     realm = (char *) strchr(instance, REALM_SEP);
347     *realm++ = 0;
348
349     for (p = 0; p <= 1; p++) {
350         for (i = 0; i <= 1; i++) {
351             for (r = 0; r <= 1; r++) {
352                 sprintf(buf, "%s%c%s%c%s", (p) ? canon : "*", INST_SEP,
353                         (i) ? instance : "*", REALM_SEP, (r) ? realm : "*");
354                 if (acl_exact_match(acl, buf))
355                     return 1;
356             }
357         }
358     }
359        
360     return(0);
361 }