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