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