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