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