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