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* 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* 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* 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* 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* 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* 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 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 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* 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 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 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 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 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* 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* 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 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* 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