ip_auth.c revision 98004
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#ifdef __sgi 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 98004 2002-06-07 08:56: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) { 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, fr, frptr) 314caddr_t data; 315int mode; 316#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) 317u_long cmd; 318#else 319int cmd; 320#endif 321frentry_t *fr, **frptr; 322{ 323 mb_t *m; 324#if defined(_KERNEL) && !SOLARIS 325 int s; 326#endif 327 frauth_t auth, *au = &auth, *fra; 328 frauthent_t *fae, **faep; 329 int i, error = 0; 330 331 switch (cmd) 332 { 333 case SIOCSTLCK : 334 error = fr_lock(data, &fr_auth_lock); 335 break; 336 case SIOCINIFR : 337 case SIOCRMIFR : 338 case SIOCADIFR : 339 error = EINVAL; 340 break; 341 case SIOCINAFR : 342 error = EINVAL; 343 break; 344 case SIOCRMAFR : 345 case SIOCADAFR : 346 for (faep = &fae_list; (fae = *faep); ) 347 if (&fae->fae_fr == fr) 348 break; 349 else 350 faep = &fae->fae_next; 351 if (cmd == SIOCRMAFR) { 352 if (!fr || !frptr) 353 error = EINVAL; 354 else if (!fae) 355 error = ESRCH; 356 else { 357 WRITE_ENTER(&ipf_auth); 358 SPL_NET(s); 359 *faep = fae->fae_next; 360 *frptr = fr->fr_next; 361 SPL_X(s); 362 RWLOCK_EXIT(&ipf_auth); 363 KFREE(fae); 364 } 365 } else if (fr && frptr) { 366 KMALLOC(fae, frauthent_t *); 367 if (fae != NULL) { 368 bcopy((char *)fr, (char *)&fae->fae_fr, 369 sizeof(*fr)); 370 WRITE_ENTER(&ipf_auth); 371 SPL_NET(s); 372 fae->fae_age = fr_defaultauthage; 373 fae->fae_fr.fr_hits = 0; 374 fae->fae_fr.fr_next = *frptr; 375 *frptr = &fae->fae_fr; 376 fae->fae_next = *faep; 377 *faep = fae; 378 ipauth = &fae_list->fae_fr; 379 SPL_X(s); 380 RWLOCK_EXIT(&ipf_auth); 381 } else 382 error = ENOMEM; 383 } else 384 error = EINVAL; 385 break; 386 case SIOCATHST: 387 fr_authstats.fas_faelist = fae_list; 388 error = IWCOPYPTR((char *)&fr_authstats, data, 389 sizeof(fr_authstats)); 390 break; 391 case SIOCAUTHW: 392 if (!(mode & FWRITE)) { 393 error = EPERM; 394 break; 395 } 396fr_authioctlloop: 397 READ_ENTER(&ipf_auth); 398 if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { 399 error = IWCOPYPTR((char *)&fr_auth[fr_authnext], data, 400 sizeof(frauth_t)); 401 RWLOCK_EXIT(&ipf_auth); 402 if (error) 403 break; 404 WRITE_ENTER(&ipf_auth); 405 SPL_NET(s); 406 fr_authnext++; 407 if (fr_authnext == FR_NUMAUTH) 408 fr_authnext = 0; 409 SPL_X(s); 410 RWLOCK_EXIT(&ipf_auth); 411 return 0; 412 } 413 RWLOCK_EXIT(&ipf_auth); 414#ifdef _KERNEL 415# if SOLARIS 416 mutex_enter(&ipf_authmx); 417 if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) { 418 mutex_exit(&ipf_authmx); 419 return EINTR; 420 } 421 mutex_exit(&ipf_authmx); 422# else 423 error = SLEEP(&fr_authnext, "fr_authnext"); 424# endif 425#endif 426 if (!error) 427 goto fr_authioctlloop; 428 break; 429 case SIOCAUTHR: 430 if (!(mode & FWRITE)) { 431 error = EPERM; 432 break; 433 } 434 error = IRCOPYPTR(data, (caddr_t)&auth, sizeof(auth)); 435 if (error) 436 return error; 437 WRITE_ENTER(&ipf_auth); 438 SPL_NET(s); 439 i = au->fra_index; 440 fra = fr_auth + i; 441 if ((i < 0) || (i > FR_NUMAUTH) || 442 (fra->fra_info.fin_id != au->fra_info.fin_id)) { 443 SPL_X(s); 444 RWLOCK_EXIT(&ipf_auth); 445 return EINVAL; 446 } 447 m = fr_authpkts[i]; 448 fra->fra_index = -2; 449 fra->fra_pass = au->fra_pass; 450 fr_authpkts[i] = NULL; 451 RWLOCK_EXIT(&ipf_auth); 452#ifdef _KERNEL 453 if (m && au->fra_info.fin_out) { 454# if SOLARIS 455 error = (fr_qout(fra->fra_q, m) == 0) ? EINVAL : 0; 456# else /* SOLARIS */ 457 struct route ro; 458 459 bzero((char *)&ro, sizeof(ro)); 460# if ((_BSDI_VERSION >= 199802) && (_BSDI_VERSION < 200005)) || \ 461 defined(__OpenBSD__) || (defined(IRIX) && (IRIX >= 605)) 462 error = ip_output(m, NULL, &ro, IP_FORWARDING, NULL, 463 NULL); 464# else 465 error = ip_output(m, NULL, &ro, IP_FORWARDING, NULL); 466# endif 467 if (ro.ro_rt) { 468 RTFREE(ro.ro_rt); 469 } 470# endif /* SOLARIS */ 471 if (error) 472 fr_authstats.fas_sendfail++; 473 else 474 fr_authstats.fas_sendok++; 475 } else if (m) { 476# if SOLARIS 477 error = (fr_qin(fra->fra_q, m) == 0) ? EINVAL : 0; 478# else /* SOLARIS */ 479 if (! IF_HANDOFF(&ipintrq, m, NULL)) 480 error = ENOBUFS; 481 else 482 schednetisr(NETISR_IP); 483# endif /* SOLARIS */ 484 if (error) 485 fr_authstats.fas_quefail++; 486 else 487 fr_authstats.fas_queok++; 488 } else 489 error = EINVAL; 490# if SOLARIS 491 if (error) 492 error = EINVAL; 493# else 494 /* 495 * If we experience an error which will result in the packet 496 * not being processed, make sure we advance to the next one. 497 */ 498 if (error == ENOBUFS) { 499 fr_authused--; 500 fra->fra_index = -1; 501 fra->fra_pass = 0; 502 if (i == fr_authstart) { 503 while (fra->fra_index == -1) { 504 i++; 505 if (i == FR_NUMAUTH) 506 i = 0; 507 fr_authstart = i; 508 if (i == fr_authend) 509 break; 510 } 511 if (fr_authstart == fr_authend) { 512 fr_authnext = 0; 513 fr_authstart = fr_authend = 0; 514 } 515 } 516 } 517# endif 518#endif /* _KERNEL */ 519 SPL_X(s); 520 break; 521 default : 522 error = EINVAL; 523 break; 524 } 525 return error; 526} 527 528 529/* 530 * Free all network buffer memory used to keep saved packets. 531 */ 532void fr_authunload() 533{ 534 register int i; 535 register frauthent_t *fae, **faep; 536 frentry_t *fr, **frp; 537 mb_t *m; 538 539 WRITE_ENTER(&ipf_auth); 540 for (i = 0; i < FR_NUMAUTH; i++) { 541 if ((m = fr_authpkts[i])) { 542 FREE_MB_T(m); 543 fr_authpkts[i] = NULL; 544 fr_auth[i].fra_index = -1; 545 } 546 } 547 548 549 for (faep = &fae_list; (fae = *faep); ) { 550 *faep = fae->fae_next; 551 KFREE(fae); 552 } 553 ipauth = NULL; 554 RWLOCK_EXIT(&ipf_auth); 555 556 if (fr_authlist) { 557 /* 558 * We *MuST* reget ipf_auth because otherwise we won't get the 559 * locks in the right order and risk deadlock. 560 * We need ipf_mutex here to prevent a rule from using it 561 * inside fr_check(). 562 */ 563 WRITE_ENTER(&ipf_mutex); 564 WRITE_ENTER(&ipf_auth); 565 for (frp = &fr_authlist; (fr = *frp); ) { 566 if (fr->fr_ref == 1) { 567 *frp = fr->fr_next; 568 KFREE(fr); 569 } else 570 frp = &fr->fr_next; 571 } 572 RWLOCK_EXIT(&ipf_auth); 573 RWLOCK_EXIT(&ipf_mutex); 574 } 575} 576 577 578/* 579 * Slowly expire held auth records. Timeouts are set 580 * in expectation of this being called twice per second. 581 */ 582void fr_authexpire() 583{ 584 register int i; 585 register frauth_t *fra; 586 register frauthent_t *fae, **faep; 587 register frentry_t *fr, **frp; 588 mb_t *m; 589#if !SOLARIS && defined(_KERNEL) 590 int s; 591#endif 592 593 if (fr_auth_lock) 594 return; 595 596 SPL_NET(s); 597 WRITE_ENTER(&ipf_auth); 598 for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { 599 if ((!--fra->fra_age) && (m = fr_authpkts[i])) { 600 FREE_MB_T(m); 601 fr_authpkts[i] = NULL; 602 fr_auth[i].fra_index = -1; 603 fr_authstats.fas_expire++; 604 fr_authused--; 605 } 606 } 607 608 for (faep = &fae_list; (fae = *faep); ) { 609 if (!--fae->fae_age) { 610 *faep = fae->fae_next; 611 KFREE(fae); 612 fr_authstats.fas_expire++; 613 } else 614 faep = &fae->fae_next; 615 } 616 if (fae_list != NULL) 617 ipauth = &fae_list->fae_fr; 618 else 619 ipauth = NULL; 620 621 for (frp = &fr_authlist; (fr = *frp); ) { 622 if (fr->fr_ref == 1) { 623 *frp = fr->fr_next; 624 KFREE(fr); 625 } else 626 frp = &fr->fr_next; 627 } 628 RWLOCK_EXIT(&ipf_auth); 629 SPL_X(s); 630} 631