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