1 #include "ratfs.h"
2 #include <ip.h>
3
4 enum {
5 ACCEPT = 0, /* verbs in control file */
6 REFUSED,
7 DENIED,
8 DIALUP,
9 BLOCKED,
10 DELAY,
11 NONE,
12
13 Subchar = '#', /* character substituted for '/' in file names */
14 };
15
16 static Keyword actions[] = {
17 "allow", ACCEPT,
18 "accept", ACCEPT,
19 "block", BLOCKED,
20 "deny", DENIED,
21 "dial", DIALUP,
22 "relay", DELAY,
23 "delay", DELAY,
24 0, NONE,
25 };
26
27 static void acctinsert(Node*, char*);
28 static char* getline(Biobuf*);
29 static void ipinsert(Node*, char*);
30 static void ipsort(void);
31
32 /*
33 * Input the configuration file
34 * Currently we only process the "ournets"
35 * specification.
36 */
37 void
getconf(void)38 getconf(void)
39 {
40 Biobuf *bp;
41 char *cp;
42 Node *np, *dir, **l;
43
44 if(debugfd >= 0)
45 fprint(debugfd, "loading %s\n", conffile);
46
47 bp = Bopen(conffile, OREAD);
48 if(bp == 0)
49 return;
50
51 dir = finddir(Trusted);
52 if(dir == 0)
53 return;
54
55 /*
56 * if this isn't the first time, purge permanent entries
57 */
58 trustedqid = Qtrustedfile;
59 if(lastconftime){
60 l = &dir->children;
61 for(np = dir->children; np; np = *l){
62 if(np->d.type == Trustedperm){
63 *l = np->sibs;
64 free(np);
65 } else {
66 np->d.qid.path = trustedqid++;
67 l = &np->sibs;
68 }
69 }
70 dir->count = 0;
71 }
72
73 for(;;){
74 cp = getline(bp);
75 if(cp == 0)
76 break;
77 if (strcmp(cp, "ournets") == 0){
78 for(cp += strlen(cp)+1; cp && *cp; cp += strlen(cp)+1){
79 np = newnode(dir, cp, Trustedperm, 0111, trustedqid++);
80 cidrparse(&np->ip, cp);
81 subslash(cp);
82 np->d.name = atom(cp);
83 }
84 }
85 }
86 Bterm(bp);
87 lastconftime = time(0);
88 }
89
90 /*
91 * Reload the control file, if necessary
92 */
93 void
reload(void)94 reload(void)
95 {
96 int type, action;
97 Biobuf *bp;
98 char *cp;
99 Node *np, *dir;
100
101 if(debugfd >= 0)
102 fprint(debugfd,"loading %s\n", ctlfile);
103
104 bp = Bopen(ctlfile, OREAD);
105 if(bp == 0)
106 return;
107
108 if(lastctltime){
109 for(dir = root->children; dir; dir = dir->sibs){
110 if (dir->d.type != Addrdir)
111 continue;
112 for(np = dir->children; np; np = np->sibs)
113 np->count = 0;
114 }
115 }
116
117 for(;;){
118 cp = getline(bp);
119 if(cp == 0)
120 break;
121 type = *cp;
122 if(type == '*'){
123 cp++;
124 if(*cp == 0) /* space before keyword */
125 cp++;
126 }
127 action = findkey(cp, actions);
128 if (action == NONE)
129 continue;
130 if (action == ACCEPT)
131 dir = dirwalk("allow", root);
132 else
133 if (action == DELAY)
134 dir = dirwalk("delay", root);
135 else
136 dir = dirwalk(cp, root);
137 if(dir == 0)
138 continue;
139
140 for(cp += strlen(cp)+1; cp && *cp; cp += strlen(cp)+1){
141 if(type == '*')
142 acctinsert(dir, cp);
143 else
144 ipinsert(dir, cp);
145 }
146 }
147 Bterm(bp);
148 ipsort();
149 dummy.d.mtime = dummy.d.atime = lastctltime = time(0);
150 }
151
152 /*
153 * get a canonicalized line: a string of null-terminated lower-case
154 * tokens with a two null bytes at the end.
155 */
156 static char*
getline(Biobuf * bp)157 getline(Biobuf *bp)
158 {
159 char c, *cp, *p, *q;
160 int n;
161
162 static char *buf;
163 static int bufsize;
164
165 for(;;){
166 cp = Brdline(bp, '\n');
167 if(cp == 0)
168 return 0;
169 n = Blinelen(bp);
170 cp[n-1] = 0;
171 if(buf == 0 || bufsize < n+1){
172 bufsize += 512;
173 if(bufsize < n+1)
174 bufsize = n+1;
175 buf = realloc(buf, bufsize);
176 if(buf == 0)
177 break;
178 }
179 q = buf;
180 for (p = cp; *p; p++){
181 c = *p;
182 if(c == '\\' && p[1]) /* we don't allow \<newline> */
183 c = *++p;
184 else
185 if(c == '#')
186 break;
187 else
188 if(c == ' ' || c == '\t' || c == ',')
189 if(q == buf || q[-1] == 0)
190 continue;
191 else
192 c = 0;
193 *q++ = tolower(c);
194 }
195 if(q != buf){
196 if(q[-1])
197 *q++ = 0;
198 *q = 0;
199 break;
200 }
201 }
202 return buf;
203 }
204
205 /*
206 * Match a keyword
207 */
208 int
findkey(char * val,Keyword * p)209 findkey(char *val, Keyword *p)
210 {
211
212 for(; p->name; p++)
213 if(strcmp(val, p->name) == 0)
214 break;
215 return p->code;
216 }
217
218 /*
219 * parse a cidr specification in either IP/mask or IP#mask format
220 */
221 void
cidrparse(Cidraddr * cidr,char * cp)222 cidrparse(Cidraddr *cidr, char *cp)
223 {
224
225 char *p, *slash;
226 int c;
227 ulong a, m;
228 uchar addr[IPv4addrlen];
229 uchar mask[IPv4addrlen];
230 char buf[64];
231
232 /*
233 * find '/' or '#' character in the cidr specification
234 */
235 slash = 0;
236 for(p = buf; p < buf+sizeof(buf)-1 && *cp; p++) {
237 c = *cp++;
238 switch(c) {
239 case Subchar:
240 c = '/';
241 slash = p;
242 break;
243 case '/':
244 slash = p;
245 break;
246 default:
247 break;
248 }
249 *p = c;
250 }
251 *p = 0;
252
253 v4parsecidr(addr, mask, buf);
254 a = nhgetl(addr);
255 m = nhgetl(mask);
256 /*
257 * if a mask isn't specified, we build a minimal mask
258 * instead of using the default mask for that net. in this
259 * case we never allow a class A mask (0xff000000).
260 */
261 if(slash == 0){
262 m = 0xff000000;
263 p = buf;
264 for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
265 m = (m>>8)|0xff000000;
266
267 /* force at least a class B */
268 m |= 0xffff0000;
269 }
270 cidr->ipaddr = a;
271 cidr->mask = m;
272 }
273
274 /*
275 * Substitute Subchar ('#') for '/'
276 */
277 char*
subslash(char * os)278 subslash(char *os)
279 {
280 char *s;
281
282 for(s=os; *s; s++)
283 if(*s == '/')
284 *s = Subchar;
285 return os;
286 }
287
288 /*
289 * Insert an account pseudo-file in a directory
290 */
291 static void
acctinsert(Node * np,char * cp)292 acctinsert(Node *np, char *cp)
293 {
294 int i;
295 char *tmp;
296 Address *ap;
297
298 static char *dangerous[] = { "*", "!", "*!", "!*", "*!*", 0 };
299
300 if(cp == 0 || *cp == 0)
301 return;
302
303 /* rule out dangerous patterns */
304 for (i = 0; dangerous[i]; i++)
305 if(strcmp(cp, dangerous[i])== 0)
306 return;
307
308 np = dirwalk("account", np);
309 if(np == 0)
310 return;
311
312 i = np->count++;
313 if(i >= np->allocated){
314 np->allocated = np->count;
315 np->addrs = realloc(np->addrs, np->allocated*sizeof(Address));
316 if(np->addrs == 0)
317 fatal("out of memory");
318 }
319
320 ap = &np->addrs[i]; /* new entry on end */
321 tmp = strdup(cp);
322 if(tmp == nil)
323 fatal("out of memory");
324 subslash(tmp);
325 ap->name = atom(tmp);
326 free(tmp);
327 }
328
329 /*
330 * Insert an IP address pseudo-file in a directory
331 */
332 static void
ipinsert(Node * np,char * cp)333 ipinsert(Node *np, char *cp)
334 {
335 char *tmp;
336 int i;
337 Address *ap;
338 if(cp == 0 || *cp == 0)
339 return;
340
341 np = dirwalk("ip", np);
342 if(np == 0)
343 return;
344
345 i = np->count++;
346 if(i >= np->allocated){
347 np->allocated = np->count;
348 np->addrs = realloc(np->addrs, np->allocated*sizeof(Address));
349 if(np->addrs == 0)
350 fatal("out of memory");
351 }
352
353 ap = &np->addrs[i]; /* new entry on end */
354 tmp = strdup(cp);
355 if(tmp == nil)
356 fatal("out of memory");
357 subslash(tmp);
358 ap->name = atom(tmp);
359 free(tmp);
360 cidrparse(&ap->ip, cp);
361 }
362
363 int
ipcomp(void * a,void * b)364 ipcomp(void *a, void *b)
365 {
366 ulong aip, bip;
367
368 aip = ((Address*)a)->ip.ipaddr;
369 bip = ((Address*)b)->ip.ipaddr;
370 if(aip > bip)
371 return 1;
372 if(aip < bip)
373 return -1;
374 return 0;
375 }
376
377 /*
378 * Sort a directory of IP addresses
379 */
380 static void
ipsort(void)381 ipsort(void)
382 {
383 int base;
384 Node *dir, *np;
385
386 base = Qaddrfile;
387 for(dir = root->children; dir; dir = dir->sibs){
388 if (dir->d.type != Addrdir)
389 continue;
390 for(np = dir->children; np; np = np->sibs){
391 if(np->d.type == IPaddr && np->count && np->addrs)
392 qsort(np->addrs, np->count, sizeof(Address), ipcomp);
393 np->baseqid = base;
394 base += np->count;
395 }
396 }
397 }
398