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