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