xref: /plan9/sys/src/cmd/ratfs/misc.c (revision 40ef9009116dd37656783aaadc8782c1d8bfb056)
1 #include "ratfs.h"
2 #include <ip.h>
3 
4 enum {
5 	Maxdoms	=	10,		/* max domains in a path */
6 	Timeout =	2*60*60,	/* seconds until temporarily trusted addr times out */
7 };
8 
9 static	int	accountmatch(char*, char**, int, char*);
10 static	Node*	acctwalk(char*,  Node*);
11 static	int	dommatch(char*, char*);
12 static	Address* ipsearch(ulong, Address*, int);
13 static	Node*	ipwalk(char*,  Node*);
14 static	Node*	trwalk(char*, Node*);
15 static	int	usermatch(char*, char*);
16 
17 /*
18  *	Do a walk
19  */
20 char*
walk(char * name,Fid * fidp)21 walk(char *name, Fid *fidp)
22 {
23 	Node *np;
24 
25 	if((fidp->node->d.mode & DMDIR) == 0)
26 		return "not a directory";
27 
28 	if(strcmp(name, ".") == 0)
29 		return 0;
30 	if(strcmp(name, "..") == 0){
31 		fidp->node = fidp->node->parent;
32 		fidp->name = 0;
33 		return 0;
34 	}
35 
36 	switch(fidp->node->d.type){
37 	case Directory:
38 	case Addrdir:
39 		np = dirwalk(name, fidp->node);
40 		break;
41 	case Trusted:
42 		np = trwalk(name, fidp->node);
43 		break;
44 	case IPaddr:
45 		np = ipwalk(name, fidp->node);
46 		break;
47 	case Acctaddr:
48 		np = acctwalk(name, fidp->node);
49 		break;
50 	default:
51 		return "directory botch in walk";
52 	}
53 	if(np) {
54 		fidp->node = np;
55 		fidp->name = np->d.name;
56 		return 0;
57 	}
58 	return "file does not exist";
59 }
60 
61 /*
62  *	Walk to a subdirectory
63  */
64 Node*
dirwalk(char * name,Node * np)65 dirwalk(char *name, Node *np)
66 {
67 	Node *p;
68 
69 	for(p = np->children; p; p = p->sibs)
70 		if(strcmp(name, p->d.name) == 0)
71 			break;
72 	return p;
73 }
74 
75 /*
76  *	Walk the directory of trusted files
77  */
78 static Node*
trwalk(char * name,Node * np)79 trwalk(char *name, Node *np)
80 {
81 	Node *p;
82 	ulong peerip;
83 	uchar addr[IPv4addrlen];
84 
85 	v4parseip(addr, name);
86 	peerip = nhgetl(addr);
87 
88 	for(p = np->children; p; p = p->sibs)
89 		if((peerip&p->ip.mask) == p->ip.ipaddr)
90 			break;
91 	return p;
92 }
93 
94 /*
95  *	Walk a directory of IP addresses
96  */
97 static Node*
ipwalk(char * name,Node * np)98 ipwalk(char *name,  Node *np)
99 {
100 	Address *ap;
101 	ulong peerip;
102 	uchar addr[IPv4addrlen];
103 
104 	v4parseip(addr, name);
105 	peerip = nhgetl(addr);
106 
107 	if(debugfd >= 0)
108 		fprint(debugfd, "%d.%d.%d.%d - ", addr[0]&0xff, addr[1]&0xff,
109 				addr[2]&0xff, addr[3]&0xff);
110 	ap = ipsearch(peerip, np->addrs, np->count);
111 	if(ap == 0)
112 		return 0;
113 
114 	dummy.d.name = ap->name;
115 	return &dummy;
116 }
117 
118 /*
119  *	Walk a directory of account names
120  */
121 static Node*
acctwalk(char * name,Node * np)122 acctwalk(char *name, Node *np)
123 {
124 	int i, n;
125 	Address *ap;
126 	char *p, *cp, *user;
127 	char buf[512];
128 	char *doms[Maxdoms];
129 
130 	strecpy(buf, buf+sizeof buf, name);
131 	subslash(buf);
132 
133 	p = buf;
134 	for(n = 0; n < Maxdoms; n++) {
135 		cp = strchr(p, '!');
136 		if(cp == 0)
137 			break;
138 		*cp = 0;
139 		doms[n] = p;
140 		p = cp+1;
141 	}
142 	user = p;
143 
144 	for(i = 0; i < np->count; i++){
145 		ap = &np->addrs[i];
146 		if (accountmatch(ap->name, doms, n, user)) {
147 			dummy.d.name = ap->name;
148 			return &dummy;
149 		}
150 	}
151 	return 0;
152 }
153 
154 /*
155  * binary search sorted IP address list
156  */
157 
158 static Address*
ipsearch(ulong addr,Address * base,int n)159 ipsearch(ulong addr, Address *base, int n)
160 {
161 	ulong top, bot, mid;
162 	Address *ap;
163 
164 	bot = 0;
165 	top = n;
166 	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
167 		ap = &base[mid];
168 		if((addr&ap->ip.mask) == ap->ip.ipaddr)
169 			return ap;
170 		if(addr < ap->ip.ipaddr)
171 			top = mid;
172 		else if(mid != n-1 && addr >= base[mid+1].ip.ipaddr)
173 			bot = mid;
174 		else
175 			break;
176 	}
177 	return 0;
178 }
179 
180 /*
181  *	Read a directory
182  */
183 int
dread(Fid * fidp,int cnt)184 dread(Fid *fidp, int cnt)
185 {
186 	uchar *q, *eq, *oq;
187 	int n, skip;
188 	Node *np;
189 
190 	if(debugfd >= 0)
191 		fprint(debugfd, "dread %d\n", cnt);
192 
193 	np = fidp->node;
194 	oq = q = rbuf+IOHDRSZ;
195 	eq = q+cnt;
196 	if(fidp->dirindex >= np->count)
197 		return 0;
198 
199 	skip = fidp->dirindex;
200 	for(np = np->children; skip > 0 && np; np = np->sibs)
201 		skip--;
202 	if(np == 0)
203 		return 0;
204 
205 	for(; q < eq && np; np = np->sibs){
206 		if(debugfd >= 0)
207 			printnode(np);
208 		if((n=convD2M(&np->d, q, eq-q)) <= BIT16SZ)
209 			break;
210 		q += n;
211 		fidp->dirindex++;
212 	}
213 	return q - oq;
214 }
215 
216 /*
217  *	Read a directory of IP addresses or account names
218  */
219 int
hread(Fid * fidp,int cnt)220 hread(Fid *fidp, int cnt)
221 {
222 	uchar *q, *eq, *oq;
223 	int i, n, path;
224 	Address *p;
225 	Node *np;
226 
227 	if(debugfd >= 0)
228 		fprint(debugfd, "hread %d\n", cnt);
229 
230 	np = fidp->node;
231 	oq = q = rbuf+IOHDRSZ;
232 	eq = q+cnt;
233 	if(fidp->dirindex >= np->count)
234 		return 0;
235 
236 	path = np->baseqid;
237 	for(i = fidp->dirindex; q < eq && i < np->count; i++){
238 		p = &np->addrs[i];
239 		dummy.d.name = p->name;
240 		dummy.d.qid.path = path++;
241 		if((n=convD2M(&dummy.d, q, eq-q)) <= BIT16SZ)
242 			break;
243 		q += n;
244 	}
245 	fidp->dirindex = i;
246 	return q - oq;
247 }
248 
249 /*
250  *	Find a directory node by type
251  */
252 Node*
finddir(int type)253 finddir(int type)
254 {
255 	Node *np;
256 
257 	for(np = root->children; np; np = np->sibs)
258 		if (np->d.type == type)
259 			return np;
260 	return 0;
261 }
262 
263 /*
264  *	Remove temporary pseudo-files that have timed-out
265  *	from the trusted directory
266  */
267 void
cleantrusted(void)268 cleantrusted(void)
269 {
270 	Node *np, **l;
271 	ulong t;
272 
273 	np = finddir(Trusted);
274 	if (np == 0)
275 		return;
276 
277 	t = time(0)-Timeout;
278 	l = &np->children;
279 	for (np = np->children; np; np = *l) {
280 		if(np->d.type == Trustedtemp && t >= np->d.mtime) {
281 			*l = np->sibs;
282 			if(debugfd >= 0)
283 				fprint(debugfd, "Deleting %s\n", np->d.name);
284 			np->parent->count--;
285 			free(np);
286 		} else
287 			l = &np->sibs;
288 	}
289 }
290 
291 /*
292  * match path components to prohibited domain & user specifications.  patterns include:
293  *	domain, domain! or domain!*	  - all users in domain
294  *	*.domain, *.domain! or *.domain!* - all users in domain and its subdomains
295  *	!user or *!user			  - user in all domains
296  *	domain!user			  - user in domain
297  *	*.domain!user			  - user in domain and its subdomains
298  *
299  *	if "user" has a trailing '*', it matches all user names beginning with "user"
300  *
301  * there are special semantics for the "domain, domain! or domain!*" specifications:
302  * the first two forms match when the domain is anywhere in at list of source-routed
303  * domains while the latter matches only when the domain is the last hop.  the same is
304  * true for the *.domain!* form of the pattern.
305  */
306 static int
accountmatch(char * spec,char ** doms,int ndoms,char * user)307 accountmatch(char *spec, char **doms, int ndoms, char *user)
308 {
309 	char *cp, *userp;
310 	int i, ret;
311 
312 	userp = 0;
313 	ret = 0;
314 	cp = strchr(spec, '!');
315 	if(cp){
316 		*cp++ = 0;		/* restored below */
317 		if(*cp)
318 		if(strcmp(cp, "*"))	/* "!*" is the same as no user field */
319 			userp = cp;	/* there is a user name */
320 	}
321 
322 	if(userp == 0){			/* no user field - domain match only */
323 		for(i = 0; i < ndoms && doms[i]; i++)
324 			if(dommatch(doms[i], spec) == 0)
325 				ret = 1;
326 	} else {
327 		/* check for "!user", "*!user" or "domain!user" */
328 		if(usermatch(user, userp) == 0){
329 			if(*spec == 0 || strcmp(spec, "*") == 0)
330 				ret = 1;
331 			else if(ndoms > 0  && dommatch(doms[ndoms-1], spec) == 0)
332 				ret = 1;
333 		}
334 	}
335 	if(cp)
336 		cp[-1] = '!';
337 	return ret;
338 }
339 
340 /*
341  *	match a user name.  the only meta-char is '*' which matches all
342  *	characters.  we only allow it as "*", which matches anything or
343  *	an * at the end of the name (e.g., "username*") which matches
344  *	trailing characters.
345  */
346 static int
usermatch(char * pathuser,char * specuser)347 usermatch(char *pathuser, char *specuser)
348 {
349 	int n;
350 
351 	n = strlen(specuser)-1;
352 	if(specuser[n] == '*'){
353 		if(n == 0)		/* match everything */
354 			return 0;
355 		return strncmp(pathuser, specuser, n);
356 	}
357 	return strcmp(pathuser, specuser);
358 }
359 
360 /*
361  *	Match a domain specification
362  */
363 static int
dommatch(char * pathdom,char * specdom)364 dommatch(char *pathdom, char *specdom)
365 {
366 	int n;
367 
368 	if (*specdom == '*'){
369 		if (specdom[1] == '.' && specdom[2]){
370 			specdom += 2;
371 			n = strlen(pathdom)-strlen(specdom);
372 			if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
373 				return strcmp(pathdom+n, specdom);
374 			return n;
375 		}
376 	}
377 	return strcmp(pathdom, specdom);
378 }
379 
380 /*
381  *	Custom allocators to avoid malloc overheads on small objects.
382  * 	We never free these.  (See below.)
383  */
384 typedef struct Stringtab	Stringtab;
385 struct Stringtab {
386 	Stringtab *link;
387 	char *str;
388 };
389 static Stringtab*
taballoc(void)390 taballoc(void)
391 {
392 	static Stringtab *t;
393 	static uint nt;
394 
395 	if(nt == 0){
396 		t = malloc(64*sizeof(Stringtab));
397 		if(t == 0)
398 			fatal("out of memory");
399 		nt = 64;
400 	}
401 	nt--;
402 	return t++;
403 }
404 
405 static char*
xstrdup(char * s)406 xstrdup(char *s)
407 {
408 	char *r;
409 	int len;
410 	static char *t;
411 	static int nt;
412 
413 	len = strlen(s)+1;
414 	if(len >= 8192)
415 		fatal("strdup big string");
416 
417 	if(nt < len){
418 		t = malloc(8192);
419 		if(t == 0)
420 			fatal("out of memory");
421 		nt = 8192;
422 	}
423 	r = t;
424 	t += len;
425 	nt -= len;
426 	strcpy(r, s);
427 	return r;
428 }
429 
430 /*
431  *	Return a uniquely allocated copy of a string.
432  *	Don't free these -- they stay in the table for the
433  *	next caller who wants that particular string.
434  *	String comparison can be done with pointer comparison
435  *	if you know both strings are atoms.
436  */
437 static Stringtab *stab[1024];
438 
439 static uint
hash(char * s)440 hash(char *s)
441 {
442 	uint h;
443 	uchar *p;
444 
445 	h = 0;
446 	for(p=(uchar*)s; *p; p++)
447 		h = h*37 + *p;
448 	return h;
449 }
450 
451 char*
atom(char * str)452 atom(char *str)
453 {
454 	uint h;
455 	Stringtab *tab;
456 
457 	h = hash(str) % nelem(stab);
458 	for(tab=stab[h]; tab; tab=tab->link)
459 		if(strcmp(str, tab->str) == 0)
460 			return tab->str;
461 
462 	tab = taballoc();
463 	tab->str = xstrdup(str);
464 	tab->link = stab[h];
465 	stab[h] = tab;
466 	return tab->str;
467 }
468