xref: /plan9/sys/src/cmd/ratfs/ctlfiles.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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