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