ip_auth.c revision 54221
1/* 2 * Copyright (C) 1998 by Darren Reed & Guido van Rooij. 3 * 4 * Redistribution and use in source and binary forms are permitted 5 * provided that this notice is preserved and due credit is given 6 * to the original author and the contributors. 7 */ 8#if !defined(lint) 9/*static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.1.2.1 1999/09/28 11:44:04 darrenr Exp $";*/ 10static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 54221 1999-12-06 20:36:50Z guido $"; 11#endif 12 13#include <sys/errno.h> 14#include <sys/types.h> 15#include <sys/param.h> 16#include <sys/time.h> 17#include <sys/file.h> 18#if !defined(_KERNEL) && !defined(KERNEL) 19# include <stdio.h> 20# include <stdlib.h> 21# include <string.h> 22#endif 23#if defined(KERNEL) && (__FreeBSD_version >= 220000) 24# include <sys/filio.h> 25# include <sys/fcntl.h> 26#else 27# include <sys/ioctl.h> 28#endif 29#include <sys/uio.h> 30#ifndef linux 31# include <sys/protosw.h> 32#endif 33#include <sys/socket.h> 34#if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) 35# include <sys/systm.h> 36#endif 37#if !defined(__SVR4) && !defined(__svr4__) 38# ifndef linux 39# include <sys/mbuf.h> 40# endif 41#else 42# include <sys/filio.h> 43# include <sys/byteorder.h> 44# ifdef _KERNEL 45# include <sys/dditypes.h> 46# endif 47# include <sys/stream.h> 48# include <sys/kmem.h> 49#endif 50#if _BSDI_VERSION >= 199802 51# include <sys/queue.h> 52#endif 53#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) 54# include <machine/cpu.h> 55#endif 56#include <net/if.h> 57#ifdef sun 58# include <net/af.h> 59#endif 60#include <net/route.h> 61#include <netinet/in.h> 62#include <netinet/in_systm.h> 63#include <netinet/ip.h> 64#ifndef KERNEL 65# define KERNEL 66# define NOT_KERNEL 67#endif 68#ifndef linux 69# include <netinet/ip_var.h> 70#endif 71#ifdef NOT_KERNEL 72# undef KERNEL 73#endif 74#ifdef __sgi 75# ifdef IFF_DRVRLOCK /* IRIX6 */ 76# include <sys/hashing.h> 77# endif 78#endif 79#include <netinet/tcp.h> 80#if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */ 81extern struct ifqueue ipintrq; /* ip packet input queue */ 82#else 83# ifndef linux 84# if __FreeBSD_version >= 300000 85# include <net/if_var.h> 86# endif 87# include <netinet/in_var.h> 88# include <netinet/tcp_fsm.h> 89# endif 90#endif 91#include <netinet/udp.h> 92#include <netinet/ip_icmp.h> 93#include "netinet/ip_compat.h" 94#include <netinet/tcpip.h> 95#include "netinet/ip_fil.h" 96#include "netinet/ip_auth.h" 97#if !SOLARIS && !defined(linux) 98# include <net/netisr.h> 99# ifdef __FreeBSD__ 100# include <machine/cpufunc.h> 101# endif 102#endif 103#if (__FreeBSD_version >= 300000) 104# include <sys/malloc.h> 105# if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) 106# include <sys/libkern.h> 107# include <sys/systm.h> 108# endif 109#endif 110 111 112 113#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 114extern KRWLOCK_T ipf_auth; 115extern kmutex_t ipf_authmx; 116# if SOLARIS 117extern kcondvar_t ipfauthwait; 118# endif 119#endif 120#ifdef linux 121static struct wait_queue *ipfauthwait = NULL; 122#endif 123 124int fr_authsize = FR_NUMAUTH; 125int fr_authused = 0; 126int fr_defaultauthage = 600; 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 134 135/* 136 * Check if a packet has authorization. If the packet is found to match an 137 * authorization result and that would result in a feedback loop (i.e. it 138 * will end up returning FR_AUTH) then return FR_BLOCK instead. 139 */ 140u_32_t fr_checkauth(ip, fin) 141ip_t *ip; 142fr_info_t *fin; 143{ 144 u_short id = ip->ip_id; 145 u_32_t pass; 146 int i; 147 148 READ_ENTER(&ipf_auth); 149 for (i = fr_authstart; i != fr_authend; ) { 150 /* 151 * index becomes -2 only after an SIOCAUTHW. Check this in 152 * case the same packet gets sent again and it hasn't yet been 153 * auth'd. 154 */ 155 if ((fr_auth[i].fra_index == -2) && 156 (id == fr_auth[i].fra_info.fin_id) && 157 !bcmp((char *)fin,(char *)&fr_auth[i].fra_info,FI_CSIZE)) { 158 /* 159 * Avoid feedback loop. 160 */ 161 if (!(pass = fr_auth[i].fra_pass) || (pass & FR_AUTH)) 162 pass = FR_BLOCK; 163 RWLOCK_EXIT(&ipf_auth); 164 WRITE_ENTER(&ipf_auth); 165 fr_authstats.fas_hits++; 166 fr_auth[i].fra_index = -1; 167 fr_authused--; 168 if (i == fr_authstart) { 169 while (fr_auth[i].fra_index == -1) { 170 i++; 171 if (i == FR_NUMAUTH) 172 i = 0; 173 fr_authstart = i; 174 if (i == fr_authend) 175 break; 176 } 177 if (fr_authstart == fr_authend) { 178 fr_authnext = 0; 179 fr_authstart = fr_authend = 0; 180 } 181 } 182 RWLOCK_EXIT(&ipf_auth); 183 return pass; 184 } 185 i++; 186 if (i == FR_NUMAUTH) 187 i = 0; 188 } 189 fr_authstats.fas_miss++; 190 RWLOCK_EXIT(&ipf_auth); 191 return 0; 192} 193 194 195/* 196 * Check if we have room in the auth array to hold details for another packet. 197 * If we do, store it and wake up any user programs which are waiting to 198 * hear about these events. 199 */ 200int fr_newauth(m, fin, ip 201#if defined(_KERNEL) && SOLARIS 202, qif) 203qif_t *qif; 204#else 205) 206#endif 207mb_t *m; 208fr_info_t *fin; 209ip_t *ip; 210{ 211 int i; 212 213 WRITE_ENTER(&ipf_auth); 214 if (fr_authstart > fr_authend) { 215 fr_authstats.fas_nospace++; 216 RWLOCK_EXIT(&ipf_auth); 217 return 0; 218 } else { 219 if ((fr_authstart == 0) && (fr_authend == FR_NUMAUTH - 1)) { 220 fr_authstats.fas_nospace++; 221 RWLOCK_EXIT(&ipf_auth); 222 return 0; 223 } 224 } 225 226 fr_authstats.fas_added++; 227 fr_authused++; 228 i = fr_authend++; 229 if (fr_authend == FR_NUMAUTH) 230 fr_authend = 0; 231 RWLOCK_EXIT(&ipf_auth); 232 fr_auth[i].fra_index = i; 233 fr_auth[i].fra_pass = 0; 234 fr_auth[i].fra_age = fr_defaultauthage; 235 bcopy((char *)fin, (char *)&fr_auth[i].fra_info, sizeof(*fin)); 236#if !defined(sparc) && !defined(m68k) 237 /* 238 * No need to copyback here as we want to undo the changes, not keep 239 * them. 240 */ 241# if SOLARIS && defined(_KERNEL) 242 if (ip == (ip_t *)m->b_rptr) 243# endif 244 { 245 register u_short bo; 246 247 bo = ip->ip_len; 248 ip->ip_len = htons(bo); 249# if !SOLARIS /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ 250 bo = ip->ip_id; 251 ip->ip_id = htons(bo); 252# endif 253 bo = ip->ip_off; 254 ip->ip_off = htons(bo); 255 } 256#endif 257#if SOLARIS && defined(_KERNEL) 258 m->b_rptr -= qif->qf_off; 259 fr_authpkts[i] = *(mblk_t **)fin->fin_mp; 260 fr_auth[i].fra_q = qif->qf_q; 261 cv_signal(&ipfauthwait); 262#else 263 fr_authpkts[i] = m; 264# if defined(linux) && defined(_KERNEL) 265 wake_up_interruptible(&ipfauthwait); 266# else 267 WAKEUP(&fr_authnext); 268# endif 269#endif 270 return 1; 271} 272 273 274int fr_auth_ioctl(data, cmd, fr, frptr) 275caddr_t data; 276#if defined(__NetBSD__) || defined(__OpenBSD__) || (FreeBSD_version >= 300003) 277u_long cmd; 278#else 279int cmd; 280#endif 281frentry_t *fr, **frptr; 282{ 283 mb_t *m; 284#if defined(_KERNEL) 285# if !SOLARIS 286 struct ifqueue *ifq; 287 int s; 288# endif 289#endif 290 frauth_t auth, *au = &auth; 291 frauthent_t *fae, **faep; 292 int i, error = 0; 293 294 switch (cmd) 295 { 296 case SIOCINIFR : 297 case SIOCRMIFR : 298 case SIOCADIFR : 299 error = EINVAL; 300 break; 301 case SIOCINAFR : 302 case SIOCRMAFR : 303 case SIOCADAFR : 304 for (faep = &fae_list; (fae = *faep); ) 305 if (&fae->fae_fr == fr) 306 break; 307 else 308 faep = &fae->fae_next; 309 if (cmd == SIOCRMAFR) { 310 if (!fae) 311 error = ESRCH; 312 else { 313 WRITE_ENTER(&ipf_auth); 314 *faep = fae->fae_next; 315 *frptr = fr->fr_next; 316 RWLOCK_EXIT(&ipf_auth); 317 KFREE(fae); 318 } 319 } else { 320 KMALLOC(fae, frauthent_t *); 321 if (fae != NULL) { 322 IRCOPY((char *)data, (char *)&fae->fae_fr, 323 sizeof(fae->fae_fr)); 324 WRITE_ENTER(&ipf_auth); 325 fae->fae_age = fr_defaultauthage; 326 fae->fae_fr.fr_hits = 0; 327 fae->fae_fr.fr_next = *frptr; 328 *frptr = &fae->fae_fr; 329 fae->fae_next = *faep; 330 *faep = fae; 331 ipauth = &fae_list->fae_fr; 332 RWLOCK_EXIT(&ipf_auth); 333 } else 334 error = ENOMEM; 335 } 336 break; 337 case SIOCATHST: 338 READ_ENTER(&ipf_auth); 339 fr_authstats.fas_faelist = fae_list; 340 RWLOCK_EXIT(&ipf_auth); 341 IWCOPY((char *)&fr_authstats, data, sizeof(fr_authstats)); 342 break; 343 case SIOCAUTHW: 344fr_authioctlloop: 345 READ_ENTER(&ipf_auth); 346 if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { 347 IWCOPY((char *)&fr_auth[fr_authnext], data, 348 sizeof(fr_info_t)); 349 RWLOCK_EXIT(&ipf_auth); 350 WRITE_ENTER(&ipf_auth); 351 fr_authnext++; 352 if (fr_authnext == FR_NUMAUTH) 353 fr_authnext = 0; 354 RWLOCK_EXIT(&ipf_auth); 355 return 0; 356 } 357#ifdef _KERNEL 358# if SOLARIS 359 mutex_enter(&ipf_authmx); 360 if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) { 361 mutex_exit(&ipf_authmx); 362 return EINTR; 363 } 364 mutex_exit(&ipf_authmx); 365# else 366# ifdef linux 367 interruptible_sleep_on(&ipfauthwait); 368 if (current->signal & ~current->blocked) 369 error = -EINTR; 370# else 371 error = SLEEP(&fr_authnext, "fr_authnext"); 372# endif 373# endif 374#endif 375 RWLOCK_EXIT(&ipf_auth); 376 if (!error) 377 goto fr_authioctlloop; 378 break; 379 case SIOCAUTHR: 380 IRCOPY(data, (caddr_t)&auth, sizeof(auth)); 381 WRITE_ENTER(&ipf_auth); 382 i = au->fra_index; 383 if ((i < 0) || (i > FR_NUMAUTH) || 384 (fr_auth[i].fra_info.fin_id != au->fra_info.fin_id)) { 385 RWLOCK_EXIT(&ipf_auth); 386 return EINVAL; 387 } 388 m = fr_authpkts[i]; 389 fr_auth[i].fra_index = -2; 390 fr_auth[i].fra_pass = au->fra_pass; 391 fr_authpkts[i] = NULL; 392#ifdef _KERNEL 393 RWLOCK_EXIT(&ipf_auth); 394 SPL_NET(s); 395# ifndef linux 396 if (m && au->fra_info.fin_out) { 397# if SOLARIS 398 error = fr_qout(fr_auth[i].fra_q, m); 399# else /* SOLARIS */ 400# if _BSDI_VERSION >= 199802 401 error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, 402 NULL); 403# else 404 error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); 405# endif 406# endif /* SOLARIS */ 407 if (error) 408 fr_authstats.fas_sendfail++; 409 else 410 fr_authstats.fas_sendok++; 411 } else if (m) { 412# if SOLARIS 413 error = fr_qin(fr_auth[i].fra_q, m); 414# else /* SOLARIS */ 415 ifq = &ipintrq; 416 if (IF_QFULL(ifq)) { 417 IF_DROP(ifq); 418 m_freem(m); 419 error = ENOBUFS; 420 } else { 421 IF_ENQUEUE(ifq, m); 422 schednetisr(NETISR_IP); 423 } 424# endif /* SOLARIS */ 425 if (error) 426 fr_authstats.fas_quefail++; 427 else 428 fr_authstats.fas_queok++; 429 } else 430 error = EINVAL; 431# endif 432# if SOLARIS 433 if (error) 434 error = EINVAL; 435# else 436 /* 437 * If we experience an error which will result in the packet 438 * not being processed, make sure we advance to the next one. 439 */ 440 if (error == ENOBUFS) { 441 fr_authused--; 442 fr_auth[i].fra_index = -1; 443 fr_auth[i].fra_pass = 0; 444 if (i == fr_authstart) { 445 while (fr_auth[i].fra_index == -1) { 446 i++; 447 if (i == FR_NUMAUTH) 448 i = 0; 449 fr_authstart = i; 450 if (i == fr_authend) 451 break; 452 } 453 if (fr_authstart == fr_authend) { 454 fr_authnext = 0; 455 fr_authstart = fr_authend = 0; 456 } 457 } 458 } 459# endif 460 SPL_X(s); 461#endif /* _KERNEL */ 462 break; 463 default : 464 error = EINVAL; 465 break; 466 } 467 return error; 468} 469 470 471#ifdef _KERNEL 472/* 473 * Free all network buffer memory used to keep saved packets. 474 */ 475void fr_authunload() 476{ 477 register int i; 478 register frauthent_t *fae, **faep; 479 mb_t *m; 480 481 WRITE_ENTER(&ipf_auth); 482 for (i = 0; i < FR_NUMAUTH; i++) { 483 if ((m = fr_authpkts[i])) { 484 FREE_MB_T(m); 485 fr_authpkts[i] = NULL; 486 fr_auth[i].fra_index = -1; 487 } 488 } 489 490 491 for (faep = &fae_list; (fae = *faep); ) { 492 *faep = fae->fae_next; 493 KFREE(fae); 494 } 495 ipauth = NULL; 496 RWLOCK_EXIT(&ipf_auth); 497} 498 499 500/* 501 * Slowly expire held auth records. Timeouts are set 502 * in expectation of this being called twice per second. 503 */ 504void fr_authexpire() 505{ 506 register int i; 507 register frauth_t *fra; 508 register frauthent_t *fae, **faep; 509 mb_t *m; 510#if !SOLARIS 511 int s; 512#endif 513 514 SPL_NET(s); 515 WRITE_ENTER(&ipf_auth); 516 for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { 517 if ((!--fra->fra_age) && (m = fr_authpkts[i])) { 518 FREE_MB_T(m); 519 fr_authpkts[i] = NULL; 520 fr_auth[i].fra_index = -1; 521 fr_authstats.fas_expire++; 522 fr_authused--; 523 } 524 } 525 526 for (faep = &fae_list; (fae = *faep); ) { 527 if (!--fae->fae_age) { 528 *faep = fae->fae_next; 529 KFREE(fae); 530 fr_authstats.fas_expire++; 531 } else 532 faep = &fae->fae_next; 533 } 534 ipauth = &fae_list->fae_fr; 535 RWLOCK_EXIT(&ipf_auth); 536 SPL_X(s); 537} 538#endif 539