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