ip_auth.c revision 130911
1/* 2 * Copyright (C) 1998-2001 by Darren Reed & Guido van Rooij. 3 * 4 * See the IPFILTER.LICENCE file for details on licencing. 5 */ 6#if defined(__sgi) && (IRIX > 602) 7# include <sys/ptimers.h> 8#endif 9#include <sys/errno.h> 10#include <sys/types.h> 11#include <sys/param.h> 12#include <sys/time.h> 13#include <sys/file.h> 14#if !defined(_KERNEL) && !defined(KERNEL) 15# include <stdio.h> 16# include <stdlib.h> 17# include <string.h> 18#endif 19#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) 20# include <sys/filio.h> 21# include <sys/fcntl.h> 22#else 23# include <sys/ioctl.h> 24#endif 25#ifndef linux 26# include <sys/protosw.h> 27#endif 28#include <sys/socket.h> 29#if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) 30# include <sys/systm.h> 31#endif 32#if !defined(__SVR4) && !defined(__svr4__) 33# ifndef linux 34# include <sys/mbuf.h> 35# endif 36#else 37# include <sys/filio.h> 38# include <sys/byteorder.h> 39# ifdef _KERNEL 40# include <sys/dditypes.h> 41# endif 42# include <sys/stream.h> 43# include <sys/kmem.h> 44#endif 45#if (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000) 46# include <sys/queue.h> 47#endif 48#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) 49# include <machine/cpu.h> 50#endif 51#include <net/if.h> 52#ifdef sun 53# include <net/af.h> 54#endif 55#include <net/route.h> 56#include <netinet/in.h> 57#include <netinet/in_systm.h> 58#include <netinet/ip.h> 59#ifndef KERNEL 60# define KERNEL 61# define NOT_KERNEL 62#endif 63#ifndef linux 64# include <netinet/ip_var.h> 65#endif 66#ifdef NOT_KERNEL 67# undef KERNEL 68#endif 69#ifdef __sgi 70# ifdef IFF_DRVRLOCK /* IRIX6 */ 71# include <sys/hashing.h> 72# endif 73#endif 74#include <netinet/tcp.h> 75#if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */ 76extern struct ifqueue ipintrq; /* ip packet input queue */ 77#else 78# ifndef linux 79# if __FreeBSD_version >= 300000 80# include <net/if_var.h> 81# endif 82# include <netinet/in_var.h> 83# include <netinet/tcp_fsm.h> 84# endif 85#endif 86#include <netinet/udp.h> 87#include <netinet/ip_icmp.h> 88#include "netinet/ip_compat.h" 89#include <netinet/tcpip.h> 90#include "netinet/ip_fil.h" 91#include "netinet/ip_auth.h" 92#if !SOLARIS && !defined(linux) 93# include <net/netisr.h> 94# ifdef __FreeBSD__ 95# include <machine/cpufunc.h> 96# endif 97#endif 98#if (__FreeBSD_version >= 300000) 99# include <sys/malloc.h> 100# if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) 101# include <sys/libkern.h> 102# include <sys/systm.h> 103# endif 104#endif 105 106#if !defined(lint) 107/* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.11.2.12 2001/07/18 14:57:08 darrenr Exp $"; */ 108static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 130911 2004-06-22 05:20:30Z darrenr $"; 109#endif 110 111 112#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 113extern KRWLOCK_T ipf_auth, ipf_mutex; 114extern kmutex_t ipf_authmx; 115# if SOLARIS 116extern kcondvar_t ipfauthwait; 117# endif 118#endif 119#ifdef linux 120static struct wait_queue *ipfauthwait = NULL; 121#endif 122 123int fr_authsize = FR_NUMAUTH; 124int fr_authused = 0; 125int fr_defaultauthage = 600; 126int fr_auth_lock = 0; 127fr_authstat_t fr_authstats; 128static frauth_t fr_auth[FR_NUMAUTH]; 129mb_t *fr_authpkts[FR_NUMAUTH]; 130static int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; 131static frauthent_t *fae_list = NULL; 132frentry_t *ipauth = NULL, 133 *fr_authlist = NULL; 134 135 136/* 137 * Check if a packet has authorization. If the packet is found to match an 138 * authorization result and that would result in a feedback loop (i.e. it 139 * will end up returning FR_AUTH) then return FR_BLOCK instead. 140 */ 141u_32_t fr_checkauth(ip, fin) 142ip_t *ip; 143fr_info_t *fin; 144{ 145 u_short id = ip->ip_id; 146 frentry_t *fr; 147 frauth_t *fra; 148 u_32_t pass; 149 int i; 150 151 if (fr_auth_lock || !fr_authused) 152 return 0; 153 154 READ_ENTER(&ipf_auth); 155 for (i = fr_authstart; i != fr_authend; ) { 156 /* 157 * index becomes -2 only after an SIOCAUTHW. Check this in 158 * case the same packet gets sent again and it hasn't yet been 159 * auth'd. 160 */ 161 fra = fr_auth + i; 162 if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) && 163 !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) { 164 /* 165 * Avoid feedback loop. 166 */ 167 if (!(pass = fra->fra_pass) || (pass & FR_AUTH)) 168 pass = FR_BLOCK; 169 /* 170 * Create a dummy rule for the stateful checking to 171 * use and return. Zero out any values we don't 172 * trust from userland! 173 */ 174 if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) && 175 (fin->fin_fi.fi_fl & FI_FRAG))) { 176 KMALLOC(fr, frentry_t *); 177 if (fr) { 178 bcopy((char *)fra->fra_info.fin_fr, 179 fr, sizeof(*fr)); 180 fr->fr_grp = NULL; 181 fr->fr_ifa = fin->fin_ifp; 182 fr->fr_func = NULL; 183 fr->fr_ref = 1; 184 fr->fr_flags = pass; 185#if BSD >= 199306 186 fr->fr_oifa = NULL; 187#endif 188 } 189 } else 190 fr = fra->fra_info.fin_fr; 191 fin->fin_fr = fr; 192 RWLOCK_EXIT(&ipf_auth); 193 WRITE_ENTER(&ipf_auth); 194 if (fr && fr != fra->fra_info.fin_fr) { 195 fr->fr_next = fr_authlist; 196 fr_authlist = fr; 197 } 198 fr_authstats.fas_hits++; 199 fra->fra_index = -1; 200 fr_authused--; 201 if (i == fr_authstart) { 202 while (fra->fra_index == -1) { 203 i++; 204 fra++; 205 if (i == FR_NUMAUTH) { 206 i = 0; 207 fra = fr_auth; 208 } 209 fr_authstart = i; 210 if (i == fr_authend) 211 break; 212 } 213 if (fr_authstart == fr_authend) { 214 fr_authnext = 0; 215 fr_authstart = fr_authend = 0; 216 } 217 } 218 RWLOCK_EXIT(&ipf_auth); 219 return pass; 220 } 221 i++; 222 if (i == FR_NUMAUTH) 223 i = 0; 224 } 225 fr_authstats.fas_miss++; 226 RWLOCK_EXIT(&ipf_auth); 227 return 0; 228} 229 230 231/* 232 * Check if we have room in the auth array to hold details for another packet. 233 * If we do, store it and wake up any user programs which are waiting to 234 * hear about these events. 235 */ 236int fr_newauth(m, fin, ip) 237mb_t *m; 238fr_info_t *fin; 239ip_t *ip; 240{ 241#if defined(_KERNEL) && SOLARIS 242 qif_t *qif = fin->fin_qif; 243#endif 244 frauth_t *fra; 245 int i; 246 247 if (fr_auth_lock) 248 return 0; 249 250 WRITE_ENTER(&ipf_auth); 251 if (fr_authstart > fr_authend) { 252 fr_authstats.fas_nospace++; 253 RWLOCK_EXIT(&ipf_auth); 254 return 0; 255 } else { 256 if (fr_authused == FR_NUMAUTH) { 257 fr_authstats.fas_nospace++; 258 RWLOCK_EXIT(&ipf_auth); 259 return 0; 260 } 261 } 262 263 fr_authstats.fas_added++; 264 fr_authused++; 265 i = fr_authend++; 266 if (fr_authend == FR_NUMAUTH) 267 fr_authend = 0; 268 RWLOCK_EXIT(&ipf_auth); 269 fra = fr_auth + i; 270 fra->fra_index = i; 271 fra->fra_pass = 0; 272 fra->fra_age = fr_defaultauthage; 273 bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin)); 274#if SOLARIS && defined(_KERNEL) 275# if !defined(sparc) 276 /* 277 * No need to copyback here as we want to undo the changes, not keep 278 * them. 279 */ 280 if ((ip == (ip_t *)m->b_rptr) && (ip->ip_v == 4)) 281 { 282 register u_short bo; 283 284 bo = ip->ip_len; 285 ip->ip_len = htons(bo); 286# if !SOLARIS && !defined(__NetBSD__) && !defined(__FreeBSD__) 287 /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ 288 bo = ip->ip_id; 289 ip->ip_id = htons(bo); 290# endif 291 bo = ip->ip_off; 292 ip->ip_off = htons(bo); 293 } 294# endif 295 m->b_rptr -= qif->qf_off; 296 fr_authpkts[i] = *(mblk_t **)fin->fin_mp; 297 fra->fra_q = qif->qf_q; 298 cv_signal(&ipfauthwait); 299#else 300# if defined(BSD) && !defined(sparc) && (BSD >= 199306) 301 if (fin->fin_out == 0) { 302 ip->ip_len = htons(ip->ip_len); 303 ip->ip_off = htons(ip->ip_off); 304 } 305# endif 306 fr_authpkts[i] = m; 307 WAKEUP(&fr_authnext); 308#endif 309 return 1; 310} 311 312 313int fr_auth_ioctl(data, mode, cmd) 314caddr_t data; 315int mode; 316#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) 317u_long cmd; 318#else 319int cmd; 320#endif 321{ 322 mb_t *m; 323#if defined(_KERNEL) && !SOLARIS 324# if !defined(__FreeBSD_version) || (__FreeBSD_version < 501104) 325 struct ifqueue *ifq; 326# endif 327 int s; 328#endif 329 frauth_t auth, *au = &auth, *fra; 330 int i, error = 0; 331 332 switch (cmd) 333 { 334 case SIOCSTLCK : 335 if (!(mode & FWRITE)) { 336 error = EPERM; 337 break; 338 } 339 error = fr_lock(data, &fr_auth_lock); 340 break; 341 case SIOCINIFR : 342 case SIOCRMIFR : 343 case SIOCADIFR : 344 error = EINVAL; 345 break; 346 case SIOCINAFR : 347 error = EINVAL; 348 break; 349 case SIOCRMAFR : 350 case SIOCADAFR : 351 /* These commands go via request to fr_preauthcmd */ 352 error = EINVAL; 353 break; 354 case SIOCATHST: 355 fr_authstats.fas_faelist = fae_list; 356 error = IWCOPYPTR((char *)&fr_authstats, data, 357 sizeof(fr_authstats)); 358 break; 359 case SIOCAUTHW: 360 if (!(mode & FWRITE)) { 361 error = EPERM; 362 break; 363 } 364fr_authioctlloop: 365 READ_ENTER(&ipf_auth); 366 if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { 367 error = IWCOPYPTR((char *)&fr_auth[fr_authnext], data, 368 sizeof(frauth_t)); 369 RWLOCK_EXIT(&ipf_auth); 370 if (error) 371 break; 372 WRITE_ENTER(&ipf_auth); 373 SPL_NET(s); 374 fr_authnext++; 375 if (fr_authnext == FR_NUMAUTH) 376 fr_authnext = 0; 377 SPL_X(s); 378 RWLOCK_EXIT(&ipf_auth); 379 return 0; 380 } 381 RWLOCK_EXIT(&ipf_auth); 382#ifdef _KERNEL 383# if SOLARIS 384 mutex_enter(&ipf_authmx); 385 if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) { 386 mutex_exit(&ipf_authmx); 387 return EINTR; 388 } 389 mutex_exit(&ipf_authmx); 390# else 391 error = SLEEP(&fr_authnext, "fr_authnext"); 392# endif 393#endif 394 if (!error) 395 goto fr_authioctlloop; 396 break; 397 case SIOCAUTHR: 398 if (!(mode & FWRITE)) { 399 error = EPERM; 400 break; 401 } 402 error = IRCOPYPTR(data, (caddr_t)&auth, sizeof(auth)); 403 if (error) 404 return error; 405 WRITE_ENTER(&ipf_auth); 406 SPL_NET(s); 407 i = au->fra_index; 408 fra = fr_auth + i; 409 if ((i < 0) || (i > FR_NUMAUTH) || 410 (fra->fra_info.fin_id != au->fra_info.fin_id)) { 411 SPL_X(s); 412 RWLOCK_EXIT(&ipf_auth); 413 return EINVAL; 414 } 415 m = fr_authpkts[i]; 416 fra->fra_index = -2; 417 fra->fra_pass = au->fra_pass; 418 fr_authpkts[i] = NULL; 419 RWLOCK_EXIT(&ipf_auth); 420#ifdef _KERNEL 421 if (m && au->fra_info.fin_out) { 422# if SOLARIS 423 error = (fr_qout(fra->fra_q, m) == 0) ? EINVAL : 0; 424# else /* SOLARIS */ 425 struct route ro; 426 427 bzero((char *)&ro, sizeof(ro)); 428# if ((_BSDI_VERSION >= 199802) && (_BSDI_VERSION < 200005)) || \ 429 defined(__OpenBSD__) || (defined(IRIX) && (IRIX >= 605)) || \ 430 (__FreeBSD_version >= 470102) 431 error = ip_output(m, NULL, &ro, IP_FORWARDING, NULL, 432 NULL); 433# else 434 error = ip_output(m, NULL, &ro, IP_FORWARDING, NULL); 435# endif 436 if (ro.ro_rt) { 437 RTFREE(ro.ro_rt); 438 } 439# endif /* SOLARIS */ 440 if (error) 441 fr_authstats.fas_sendfail++; 442 else 443 fr_authstats.fas_sendok++; 444 } else if (m) { 445# if SOLARIS 446 error = (fr_qin(fra->fra_q, m) == 0) ? EINVAL : 0; 447# else /* SOLARIS */ 448 if (! netisr_queue(NETISR_IP, m)) 449 error = ENOBUFS; 450# endif /* SOLARIS */ 451 if (error) 452 fr_authstats.fas_quefail++; 453 else 454 fr_authstats.fas_queok++; 455 } else 456 error = EINVAL; 457# if SOLARIS 458 if (error) 459 error = EINVAL; 460# else 461 /* 462 * If we experience an error which will result in the packet 463 * not being processed, make sure we advance to the next one. 464 */ 465 if (error == ENOBUFS) { 466 fr_authused--; 467 fra->fra_index = -1; 468 fra->fra_pass = 0; 469 if (i == fr_authstart) { 470 while (fra->fra_index == -1) { 471 i++; 472 if (i == FR_NUMAUTH) 473 i = 0; 474 fr_authstart = i; 475 if (i == fr_authend) 476 break; 477 } 478 if (fr_authstart == fr_authend) { 479 fr_authnext = 0; 480 fr_authstart = fr_authend = 0; 481 } 482 } 483 } 484# endif 485#endif /* _KERNEL */ 486 SPL_X(s); 487 break; 488 default : 489 error = EINVAL; 490 break; 491 } 492 return error; 493} 494 495 496/* 497 * Free all network buffer memory used to keep saved packets. 498 */ 499void fr_authunload() 500{ 501 register int i; 502 register frauthent_t *fae, **faep; 503 frentry_t *fr, **frp; 504 mb_t *m; 505 506 WRITE_ENTER(&ipf_auth); 507 for (i = 0; i < FR_NUMAUTH; i++) { 508 if ((m = fr_authpkts[i])) { 509 FREE_MB_T(m); 510 fr_authpkts[i] = NULL; 511 fr_auth[i].fra_index = -1; 512 } 513 } 514 515 516 for (faep = &fae_list; (fae = *faep); ) { 517 *faep = fae->fae_next; 518 KFREE(fae); 519 } 520 ipauth = NULL; 521 RWLOCK_EXIT(&ipf_auth); 522 523 if (fr_authlist) { 524 /* 525 * We *MuST* reget ipf_auth because otherwise we won't get the 526 * locks in the right order and risk deadlock. 527 * We need ipf_mutex here to prevent a rule from using it 528 * inside fr_check(). 529 */ 530 WRITE_ENTER(&ipf_mutex); 531 WRITE_ENTER(&ipf_auth); 532 for (frp = &fr_authlist; (fr = *frp); ) { 533 if (fr->fr_ref == 1) { 534 *frp = fr->fr_next; 535 KFREE(fr); 536 } else 537 frp = &fr->fr_next; 538 } 539 RWLOCK_EXIT(&ipf_auth); 540 RWLOCK_EXIT(&ipf_mutex); 541 } 542} 543 544 545/* 546 * Slowly expire held auth records. Timeouts are set 547 * in expectation of this being called twice per second. 548 */ 549void fr_authexpire() 550{ 551 register int i; 552 register frauth_t *fra; 553 register frauthent_t *fae, **faep; 554 register frentry_t *fr, **frp; 555 mb_t *m; 556#if !SOLARIS && defined(_KERNEL) 557 int s; 558#endif 559 560 if (fr_auth_lock) 561 return; 562 563 SPL_NET(s); 564 WRITE_ENTER(&ipf_auth); 565 for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { 566 if ((!--fra->fra_age) && (m = fr_authpkts[i])) { 567 FREE_MB_T(m); 568 fr_authpkts[i] = NULL; 569 fr_auth[i].fra_index = -1; 570 fr_authstats.fas_expire++; 571 fr_authused--; 572 } 573 } 574 575 for (faep = &fae_list; (fae = *faep); ) { 576 if (!--fae->fae_age) { 577 *faep = fae->fae_next; 578 KFREE(fae); 579 fr_authstats.fas_expire++; 580 } else 581 faep = &fae->fae_next; 582 } 583 if (fae_list != NULL) 584 ipauth = &fae_list->fae_fr; 585 else 586 ipauth = NULL; 587 588 for (frp = &fr_authlist; (fr = *frp); ) { 589 if (fr->fr_ref == 1) { 590 *frp = fr->fr_next; 591 KFREE(fr); 592 } else 593 frp = &fr->fr_next; 594 } 595 RWLOCK_EXIT(&ipf_auth); 596 SPL_X(s); 597} 598 599int fr_preauthcmd(cmd, fr, frptr) 600#if defined(__NetBSD__) || defined(__OpenBSD__) || \ 601 (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) 602u_long cmd; 603#else 604int cmd; 605#endif 606frentry_t *fr, **frptr; 607{ 608 frauthent_t *fae, **faep; 609 int error = 0; 610#if defined(KERNEL) && !SOLARIS 611 int s; 612#endif 613 614 if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) { 615 /* Should not happen */ 616 printf("fr_preauthcmd called with bad cmd 0x%lx", (u_long)cmd); 617 return EIO; 618 } 619 620 for (faep = &fae_list; (fae = *faep); ) 621 if (&fae->fae_fr == fr) 622 break; 623 else 624 faep = &fae->fae_next; 625 if (cmd == SIOCRMAFR) { 626 if (!fr || !frptr) 627 error = EINVAL; 628 else if (!fae) 629 error = ESRCH; 630 else { 631 WRITE_ENTER(&ipf_auth); 632 SPL_NET(s); 633 *faep = fae->fae_next; 634 *frptr = fr->fr_next; 635 SPL_X(s); 636 RWLOCK_EXIT(&ipf_auth); 637 KFREE(fae); 638 } 639 } else if (fr && frptr) { 640 KMALLOC(fae, frauthent_t *); 641 if (fae != NULL) { 642 bcopy((char *)fr, (char *)&fae->fae_fr, 643 sizeof(*fr)); 644 WRITE_ENTER(&ipf_auth); 645 SPL_NET(s); 646 fae->fae_age = fr_defaultauthage; 647 fae->fae_fr.fr_hits = 0; 648 fae->fae_fr.fr_next = *frptr; 649 *frptr = &fae->fae_fr; 650 fae->fae_next = *faep; 651 *faep = fae; 652 ipauth = &fae_list->fae_fr; 653 SPL_X(s); 654 RWLOCK_EXIT(&ipf_auth); 655 } else 656 error = ENOMEM; 657 } else 658 error = EINVAL; 659 return error; 660} 661