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