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