1 /* $NetBSD: ip_scan.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $ */ 2 3 /* 4 * Copyright (C) 2012 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 */ 8 #if defined(KERNEL) || defined(_KERNEL) 9 # undef KERNEL 10 # undef _KERNEL 11 # define KERNEL 1 12 # define _KERNEL 1 13 #endif 14 #include <sys/param.h> 15 #if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL) 16 # include <sys/kern_svcs.h> 17 #endif 18 #include <sys/types.h> 19 #include <sys/time.h> 20 #include <sys/errno.h> 21 #if !defined(_KERNEL) 22 # include <stdlib.h> 23 # include <string.h> 24 # define _KERNEL 25 # ifdef __OpenBSD__ 26 struct file; 27 # endif 28 # include <sys/uio.h> 29 # undef _KERNEL 30 #else 31 # include <sys/systm.h> 32 # if !defined(__svr4__) && !defined(__SVR4) 33 # include <sys/mbuf.h> 34 # endif 35 #endif 36 #include <sys/socket.h> 37 #if !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(AIX) 38 # include <sys/ioccom.h> 39 #endif 40 #ifdef __FreeBSD__ 41 # include <sys/filio.h> 42 # include <sys/malloc.h> 43 #else 44 # include <sys/ioctl.h> 45 #endif 46 47 #include <netinet/in.h> 48 #include <netinet/in_systm.h> 49 #include <netinet/ip.h> 50 #include <netinet/tcp.h> 51 52 #include <net/if.h> 53 54 55 #include "netinet/ip_compat.h" 56 #include "netinet/ip_fil.h" 57 #include "netinet/ip_state.h" 58 #include "netinet/ip_scan.h" 59 /* END OF INCLUDES */ 60 61 #if !defined(lint) 62 #if defined(__NetBSD__) 63 #include <sys/cdefs.h> 64 __KERNEL_RCSID(0, "$NetBSD: ip_scan.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $"); 65 #else 66 static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; 67 static const char rcsid[] = "@(#)Id: ip_scan.c,v 1.1.1.2 2012/07/22 13:45:34 darrenr Exp"; 68 #endif 69 #endif 70 71 #ifdef IPFILTER_SCAN /* endif at bottom of file */ 72 73 74 ipscan_t *ipf_scan_list = NULL, 75 *ipf_scan_tail = NULL; 76 ipscanstat_t ipf_scan_stat; 77 # ifdef USE_MUTEXES 78 ipfrwlock_t ipf_scan_rwlock; 79 # endif 80 81 # ifndef isalpha 82 # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \ 83 ((x) >= 'a' && 'z' >= (x))) 84 # endif 85 86 87 int ipf_scan_add(void *); 88 int ipf_scan_remove(void *); 89 struct ipscan *ipf_scan_lookup(char *); 90 int ipf_scan_matchstr(sinfo_t *, char *, int); 91 int ipf_scan_matchisc(ipscan_t *, ipstate_t *, int, int, int *); 92 int ipf_scan_match(ipstate_t *); 93 94 static int ipf_scan_inited = 0; 95 96 97 int 98 ipf_scan_init() 99 { 100 RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock"); 101 ipf_scan_inited = 1; 102 return 0; 103 } 104 105 106 void 107 ipf_scan_unload(void *arg) 108 { 109 if (ipf_scan_inited == 1) { 110 RW_DESTROY(&ipf_scan_rwlock); 111 ipf_scan_inited = 0; 112 } 113 } 114 115 116 int 117 ipf_scan_add(data) 118 void *data; 119 { 120 ipscan_t *i, *isc; 121 int err; 122 123 KMALLOC(isc, ipscan_t *); 124 if (!isc) { 125 ipf_interror = 90001; 126 return ENOMEM; 127 } 128 129 err = copyinptr(data, isc, sizeof(*isc)); 130 if (err) { 131 KFREE(isc); 132 return err; 133 } 134 135 WRITE_ENTER(&ipf_scan_rwlock); 136 137 i = ipf_scan_lookup(isc->ipsc_tag); 138 if (i != NULL) { 139 RWLOCK_EXIT(&ipf_scan_rwlock); 140 KFREE(isc); 141 ipf_interror = 90002; 142 return EEXIST; 143 } 144 145 if (ipf_scan_tail) { 146 ipf_scan_tail->ipsc_next = isc; 147 isc->ipsc_pnext = &ipf_scan_tail->ipsc_next; 148 ipf_scan_tail = isc; 149 } else { 150 ipf_scan_list = isc; 151 ipf_scan_tail = isc; 152 isc->ipsc_pnext = &ipf_scan_list; 153 } 154 isc->ipsc_next = NULL; 155 156 isc->ipsc_hits = 0; 157 isc->ipsc_fref = 0; 158 isc->ipsc_sref = 0; 159 isc->ipsc_active = 0; 160 161 ipf_scan_stat.iscs_entries++; 162 RWLOCK_EXIT(&ipf_scan_rwlock); 163 return 0; 164 } 165 166 167 int 168 ipf_scan_remove(data) 169 void *data; 170 { 171 ipscan_t isc, *i; 172 int err; 173 174 err = copyinptr(data, &isc, sizeof(isc)); 175 if (err) 176 return err; 177 178 WRITE_ENTER(&ipf_scan_rwlock); 179 180 i = ipf_scan_lookup(isc.ipsc_tag); 181 if (i == NULL) 182 err = ENOENT; 183 else { 184 if (i->ipsc_fref) { 185 RWLOCK_EXIT(&ipf_scan_rwlock); 186 ipf_interror = 90003; 187 return EBUSY; 188 } 189 190 *i->ipsc_pnext = i->ipsc_next; 191 if (i->ipsc_next) 192 i->ipsc_next->ipsc_pnext = i->ipsc_pnext; 193 else { 194 if (i->ipsc_pnext == &ipf_scan_list) 195 ipf_scan_tail = NULL; 196 else 197 ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext; 198 } 199 200 ipf_scan_stat.iscs_entries--; 201 KFREE(i); 202 } 203 RWLOCK_EXIT(&ipf_scan_rwlock); 204 return err; 205 } 206 207 208 struct ipscan * 209 ipf_scan_lookup(tag) 210 char *tag; 211 { 212 ipscan_t *i; 213 214 for (i = ipf_scan_list; i; i = i->ipsc_next) 215 if (!strcmp(i->ipsc_tag, tag)) 216 return i; 217 return NULL; 218 } 219 220 221 int 222 ipf_scan_attachfr(fr) 223 struct frentry *fr; 224 { 225 ipscan_t *i; 226 227 if (fr->fr_isctag != -1) { 228 READ_ENTER(&ipf_scan_rwlock); 229 i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names); 230 if (i != NULL) { 231 ATOMIC_INC32(i->ipsc_fref); 232 } 233 RWLOCK_EXIT(&ipf_scan_rwlock); 234 if (i == NULL) { 235 ipf_interror = 90004; 236 return ENOENT; 237 } 238 fr->fr_isc = i; 239 } 240 return 0; 241 } 242 243 244 int 245 ipf_scan_attachis(is) 246 struct ipstate *is; 247 { 248 frentry_t *fr; 249 ipscan_t *i; 250 251 READ_ENTER(&ipf_scan_rwlock); 252 fr = is->is_rule; 253 if (fr != NULL) { 254 i = fr->fr_isc; 255 if ((i != NULL) && (i != (ipscan_t *)-1)) { 256 is->is_isc = i; 257 ATOMIC_INC32(i->ipsc_sref); 258 if (i->ipsc_clen) 259 is->is_flags |= IS_SC_CLIENT; 260 else 261 is->is_flags |= IS_SC_MATCHC; 262 if (i->ipsc_slen) 263 is->is_flags |= IS_SC_SERVER; 264 else 265 is->is_flags |= IS_SC_MATCHS; 266 } 267 } 268 RWLOCK_EXIT(&ipf_scan_rwlock); 269 return 0; 270 } 271 272 273 int 274 ipf_scan_detachfr(fr) 275 struct frentry *fr; 276 { 277 ipscan_t *i; 278 279 i = fr->fr_isc; 280 if (i != NULL) { 281 ATOMIC_DEC32(i->ipsc_fref); 282 } 283 return 0; 284 } 285 286 287 int 288 ipf_scan_detachis(is) 289 struct ipstate *is; 290 { 291 ipscan_t *i; 292 293 READ_ENTER(&ipf_scan_rwlock); 294 if ((i = is->is_isc) && (i != (ipscan_t *)-1)) { 295 ATOMIC_DEC32(i->ipsc_sref); 296 is->is_isc = NULL; 297 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER); 298 } 299 RWLOCK_EXIT(&ipf_scan_rwlock); 300 return 0; 301 } 302 303 304 /* 305 * 'string' compare for scanning 306 */ 307 int 308 ipf_scan_matchstr(sp, str, n) 309 sinfo_t *sp; 310 char *str; 311 int n; 312 { 313 char *s, *t, *up; 314 int i = n; 315 316 if (i > sp->s_len) 317 i = sp->s_len; 318 up = str; 319 320 for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++) 321 switch ((int)*t) 322 { 323 case '.' : 324 if (*s != *up) 325 return 1; 326 break; 327 case '?' : 328 if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f))) 329 return 1; 330 break; 331 case '*' : 332 break; 333 } 334 return 0; 335 } 336 337 338 /* 339 * Returns 3 if both server and client match, 2 if just server, 340 * 1 if just client 341 */ 342 int 343 ipf_scan_matchisc(isc, is, cl, sl, maxm) 344 ipscan_t *isc; 345 ipstate_t *is; 346 int cl, sl, maxm[2]; 347 { 348 int i, j, k, n, ret = 0, flags; 349 350 flags = is->is_flags; 351 352 /* 353 * If we've already matched more than what is on offer, then 354 * assume we have a better match already and forget this one. 355 */ 356 if (maxm != NULL) { 357 if (isc->ipsc_clen < maxm[0]) 358 return 0; 359 if (isc->ipsc_slen < maxm[1]) 360 return 0; 361 j = maxm[0]; 362 k = maxm[1]; 363 } else { 364 j = 0; 365 k = 0; 366 } 367 368 if (!isc->ipsc_clen) 369 ret = 1; 370 else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) && 371 cl && isc->ipsc_clen) { 372 i = 0; 373 n = MIN(cl, isc->ipsc_clen); 374 if ((n > 0) && (!maxm || (n >= maxm[1]))) { 375 if (!ipf_scan_matchstr(&isc->ipsc_cl, 376 is->is_sbuf[0], n)) { 377 i++; 378 ret |= 1; 379 if (n > j) 380 j = n; 381 } 382 } 383 } 384 385 if (!isc->ipsc_slen) 386 ret |= 2; 387 else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) && 388 sl && isc->ipsc_slen) { 389 i = 0; 390 n = MIN(cl, isc->ipsc_slen); 391 if ((n > 0) && (!maxm || (n >= maxm[1]))) { 392 if (!ipf_scan_matchstr(&isc->ipsc_sl, 393 is->is_sbuf[1], n)) { 394 i++; 395 ret |= 2; 396 if (n > k) 397 k = n; 398 } 399 } 400 } 401 402 if (maxm && (ret == 3)) { 403 maxm[0] = j; 404 maxm[1] = k; 405 } 406 return ret; 407 } 408 409 410 int 411 ipf_scan_match(is) 412 ipstate_t *is; 413 { 414 int i, j, k, n, cl, sl, maxm[2]; 415 ipscan_t *isc, *lm; 416 tcpdata_t *t; 417 418 for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1) 419 cl++; 420 for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1) 421 sl++; 422 423 j = 0; 424 isc = is->is_isc; 425 if (isc != NULL) { 426 /* 427 * Known object to scan for. 428 */ 429 i = ipf_scan_matchisc(isc, is, cl, sl, NULL); 430 if (i & 1) { 431 is->is_flags |= IS_SC_MATCHC; 432 is->is_flags &= ~IS_SC_CLIENT; 433 } else if (cl >= isc->ipsc_clen) 434 is->is_flags &= ~IS_SC_CLIENT; 435 if (i & 2) { 436 is->is_flags |= IS_SC_MATCHS; 437 is->is_flags &= ~IS_SC_SERVER; 438 } else if (sl >= isc->ipsc_slen) 439 is->is_flags &= ~IS_SC_SERVER; 440 } else { 441 i = 0; 442 lm = NULL; 443 maxm[0] = 0; 444 maxm[1] = 0; 445 for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) { 446 i = ipf_scan_matchisc(isc, is, cl, sl, maxm); 447 if (i) { 448 /* 449 * We only want to remember the best match 450 * and the number of times we get a best 451 * match. 452 */ 453 if ((j == 3) && (i < 3)) 454 continue; 455 if ((i == 3) && (j != 3)) 456 k = 1; 457 else 458 k++; 459 j = i; 460 lm = isc; 461 } 462 } 463 if (k == 1) 464 isc = lm; 465 if (isc == NULL) 466 return 0; 467 468 /* 469 * No matches or partial matches, so reset the respective 470 * search flag. 471 */ 472 if (!(j & 1)) 473 is->is_flags &= ~IS_SC_CLIENT; 474 475 if (!(j & 2)) 476 is->is_flags &= ~IS_SC_SERVER; 477 478 /* 479 * If we found the best match, then set flags appropriately. 480 */ 481 if ((j == 3) && (k == 1)) { 482 is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT); 483 is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC); 484 } 485 } 486 487 /* 488 * If the acknowledged side of a connection has moved past the data in 489 * which we are interested, then reset respective flag. 490 */ 491 t = &is->is_tcp.ts_data[0]; 492 if (t->td_end > is->is_s0[0] + 15) 493 is->is_flags &= ~IS_SC_CLIENT; 494 495 t = &is->is_tcp.ts_data[1]; 496 if (t->td_end > is->is_s0[1] + 15) 497 is->is_flags &= ~IS_SC_SERVER; 498 499 /* 500 * Matching complete ? 501 */ 502 j = ISC_A_NONE; 503 if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) { 504 j = isc->ipsc_action; 505 ipf_scan_stat.iscs_acted++; 506 } else if ((is->is_isc != NULL) && 507 ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) && 508 !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) { 509 /* 510 * Matching failed... 511 */ 512 j = isc->ipsc_else; 513 ipf_scan_stat.iscs_else++; 514 } 515 516 switch (j) 517 { 518 case ISC_A_CLOSE : 519 /* 520 * If as a result of a successful match we are to 521 * close a connection, change the "keep state" info. 522 * to block packets and generate TCP RST's. 523 */ 524 is->is_pass &= ~FR_RETICMP; 525 is->is_pass |= FR_RETRST; 526 break; 527 default : 528 break; 529 } 530 531 return i; 532 } 533 534 535 /* 536 * check if a packet matches what we're scanning for 537 */ 538 int 539 ipf_scan_packet(fin, is) 540 fr_info_t *fin; 541 ipstate_t *is; 542 { 543 int i, j, rv, dlen, off, thoff; 544 u_32_t seq, s0; 545 tcphdr_t *tcp; 546 547 rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src); 548 tcp = fin->fin_dp; 549 seq = ntohl(tcp->th_seq); 550 551 if (!is->is_s0[rv]) 552 return 1; 553 554 /* 555 * check if this packet has more data that falls within the first 556 * 16 bytes sent in either direction. 557 */ 558 s0 = is->is_s0[rv]; 559 off = seq - s0; 560 if ((off > 15) || (off < 0)) 561 return 1; 562 thoff = TCP_OFF(tcp) << 2; 563 dlen = fin->fin_dlen - thoff; 564 if (dlen <= 0) 565 return 1; 566 if (dlen > 16) 567 dlen = 16; 568 if (off + dlen > 16) 569 dlen = 16 - off; 570 571 j = 0xffff >> (16 - dlen); 572 i = (0xffff & j) << off; 573 #ifdef _KERNEL 574 COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff, 575 dlen, (void *)is->is_sbuf[rv] + off); 576 #endif 577 is->is_smsk[rv] |= i; 578 for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1) 579 j++; 580 if (j == 0) 581 return 1; 582 583 (void) ipf_scan_match(is); 584 #if 0 585 /* 586 * There is the potential here for plain text passwords to get 587 * buffered and stored for some time... 588 */ 589 if (!(is->is_flags & IS_SC_CLIENT)) 590 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0])); 591 if (!(is->is_flags & IS_SC_SERVER)) 592 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1])); 593 #endif 594 return 0; 595 } 596 597 598 int 599 ipf_scan_ioctl(data, cmd, mode, uid, ctx) 600 void *data; 601 ioctlcmd_t cmd; 602 int mode, uid; 603 void *ctx; 604 { 605 ipscanstat_t ipscs; 606 int err = 0; 607 608 switch (cmd) 609 { 610 case SIOCADSCA : 611 err = ipf_scan_add(data); 612 break; 613 case SIOCRMSCA : 614 err = ipf_scan_remove(data); 615 break; 616 case SIOCGSCST : 617 bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs)); 618 ipscs.iscs_list = ipf_scan_list; 619 err = BCOPYOUT(&ipscs, data, sizeof(ipscs)); 620 if (err != 0) { 621 ipf_interror = 90005; 622 err = EFAULT; 623 } 624 break; 625 default : 626 err = EINVAL; 627 break; 628 } 629 630 return err; 631 } 632 #endif /* IPFILTER_SCAN */ 633