1
2/*
3 * Copyright (C) 2012 by Darren Reed.
4 *
5 * See the IPFILTER.LICENCE file for details on licencing.
6 */
7#if defined(KERNEL) || defined(_KERNEL)
8# undef KERNEL
9# undef _KERNEL
10# define        KERNEL	1
11# define        _KERNEL	1
12#endif
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)
19# include <stdio.h>
20# include <stdlib.h>
21# ifdef _STDC_C99
22#  include <stdbool.h>
23# endif
24# include <string.h>
25# define _KERNEL
26# include <sys/uio.h>
27# undef _KERNEL
28#endif
29#if defined(_KERNEL) && defined(__FreeBSD__)
30# include <sys/filio.h>
31# include <sys/fcntl.h>
32#else
33# include <sys/ioctl.h>
34#endif
35# include <sys/protosw.h>
36#include <sys/socket.h>
37#if defined(_KERNEL)
38# include <sys/systm.h>
39# if !defined(__SVR4)
40#  include <sys/mbuf.h>
41# endif
42#endif
43#if defined(__SVR4)
44# include <sys/filio.h>
45# include <sys/byteorder.h>
46# ifdef _KERNEL
47#  include <sys/dditypes.h>
48# endif
49# include <sys/stream.h>
50# include <sys/kmem.h>
51#endif
52#if defined(__FreeBSD__)
53# include <sys/queue.h>
54#endif
55#if defined(__NetBSD__)
56# include <machine/cpu.h>
57#endif
58#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
59# include <sys/proc.h>
60#endif
61#if defined(__NetBSD_Version__) &&  (__NetBSD_Version__ >= 400000) && \
62     !defined(_KERNEL)
63# include <stdbool.h>
64#endif
65#include <net/if.h>
66#ifdef sun
67# include <net/af.h>
68#endif
69#include <netinet/in.h>
70#include <netinet/in_systm.h>
71#include <netinet/ip.h>
72# include <netinet/ip_var.h>
73#if !defined(_KERNEL)
74# define	KERNEL
75# define	_KERNEL
76# define	NOT_KERNEL
77#endif
78#ifdef	NOT_KERNEL
79# undef	_KERNEL
80# undef	KERNEL
81#endif
82#include <netinet/tcp.h>
83#if defined(__FreeBSD__)
84# include <net/if_var.h>
85# define IF_QFULL _IF_QFULL
86# define IF_DROP _IF_DROP
87#endif
88#include <netinet/in_var.h>
89#include <netinet/tcp_fsm.h>
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
97# include <net/netisr.h>
98# ifdef __FreeBSD__
99#  include <machine/cpufunc.h>
100# endif
101#endif
102#if defined(__FreeBSD__)
103# include <sys/malloc.h>
104# if defined(_KERNEL) && !defined(IPFILTER_LKM)
105#  include <sys/libkern.h>
106#  include <sys/systm.h>
107# endif
108#endif
109/* END OF INCLUDES */
110
111
112
113static void ipf_auth_deref(frauthent_t **);
114static void ipf_auth_deref_unlocked(ipf_auth_softc_t *, frauthent_t **);
115static int ipf_auth_geniter(ipf_main_softc_t *, ipftoken_t *,
116				 ipfgeniter_t *, ipfobj_t *);
117static int ipf_auth_reply(ipf_main_softc_t *, ipf_auth_softc_t *, char *);
118static int ipf_auth_wait(ipf_main_softc_t *, ipf_auth_softc_t *, char *);
119static int ipf_auth_flush(void *);
120
121
122/* ------------------------------------------------------------------------ */
123/* Function:    ipf_auth_main_load                                          */
124/* Returns:     int - 0 == success, else error                              */
125/* Parameters:  None                                                        */
126/*                                                                          */
127/* A null-op function that exists as a placeholder so that the flow in      */
128/* other functions is obvious.                                              */
129/* ------------------------------------------------------------------------ */
130int
131ipf_auth_main_load(void)
132{
133	return (0);
134}
135
136
137/* ------------------------------------------------------------------------ */
138/* Function:    ipf_auth_main_unload                                        */
139/* Returns:     int - 0 == success, else error                              */
140/* Parameters:  None                                                        */
141/*                                                                          */
142/* A null-op function that exists as a placeholder so that the flow in      */
143/* other functions is obvious.                                              */
144/* ------------------------------------------------------------------------ */
145int
146ipf_auth_main_unload(void)
147{
148	return (0);
149}
150
151
152/* ------------------------------------------------------------------------ */
153/* Function:    ipf_auth_soft_create                                        */
154/* Returns:     int - NULL = failure, else success                          */
155/* Parameters:  softc(I) - pointer to soft context data                     */
156/*                                                                          */
157/* Create a structre to store all of the run-time data for packet auth in   */
158/* and initialise some fields to their defaults.                            */
159/* ------------------------------------------------------------------------ */
160void *
161ipf_auth_soft_create(ipf_main_softc_t *softc)
162{
163	ipf_auth_softc_t *softa;
164
165	KMALLOC(softa, ipf_auth_softc_t *);
166	if (softa == NULL)
167		return (NULL);
168
169	bzero((char *)softa, sizeof(*softa));
170
171	softa->ipf_auth_size = FR_NUMAUTH;
172	softa->ipf_auth_defaultage = 600;
173
174	RWLOCK_INIT(&softa->ipf_authlk, "ipf IP User-Auth rwlock");
175	MUTEX_INIT(&softa->ipf_auth_mx, "ipf auth log mutex");
176#if SOLARIS && defined(_KERNEL)
177	cv_init(&softa->ipf_auth_wait, "ipf auth condvar", CV_DRIVER, NULL);
178#endif
179
180	return (softa);
181}
182
183/* ------------------------------------------------------------------------ */
184/* Function:    ipf_auth_soft_init                                          */
185/* Returns:     int - 0 == success, else error                              */
186/* Parameters:  softc(I) - pointer to soft context data                     */
187/*              arg(I)   - opaque pointer to auth context data              */
188/*                                                                          */
189/* Allocate memory and initialise data structures used in handling auth     */
190/* rules.                                                                   */
191/* ------------------------------------------------------------------------ */
192int
193ipf_auth_soft_init(ipf_main_softc_t *softc, void *arg)
194{
195	ipf_auth_softc_t *softa = arg;
196
197	KMALLOCS(softa->ipf_auth, frauth_t *,
198		 softa->ipf_auth_size * sizeof(*softa->ipf_auth));
199	if (softa->ipf_auth == NULL)
200		return (-1);
201	bzero((char *)softa->ipf_auth,
202	      softa->ipf_auth_size * sizeof(*softa->ipf_auth));
203
204	KMALLOCS(softa->ipf_auth_pkts, mb_t **,
205		 softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts));
206	if (softa->ipf_auth_pkts == NULL)
207		return (-2);
208	bzero((char *)softa->ipf_auth_pkts,
209	      softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts));
210
211
212	return (0);
213}
214
215
216/* ------------------------------------------------------------------------ */
217/* Function:    ipf_auth_soft_fini                                          */
218/* Returns:     int - 0 == success, else error                              */
219/* Parameters:  softc(I) - pointer to soft context data                     */
220/*              arg(I)   - opaque pointer to auth context data              */
221/*                                                                          */
222/* Free all network buffer memory used to keep saved packets that have been */
223/* connectedd to the soft soft context structure *but* do not free that: it */
224/* is free'd by _destroy().                                                 */
225/* ------------------------------------------------------------------------ */
226int
227ipf_auth_soft_fini(ipf_main_softc_t *softc, void *arg)
228{
229	ipf_auth_softc_t *softa = arg;
230	frauthent_t *fae, **faep;
231	frentry_t *fr, **frp;
232	mb_t *m;
233	int i;
234
235	if (softa->ipf_auth != NULL) {
236		KFREES(softa->ipf_auth,
237		       softa->ipf_auth_size * sizeof(*softa->ipf_auth));
238		softa->ipf_auth = NULL;
239	}
240
241	if (softa->ipf_auth_pkts != NULL) {
242		for (i = 0; i < softa->ipf_auth_size; i++) {
243			m = softa->ipf_auth_pkts[i];
244			if (m != NULL) {
245				FREE_MB_T(m);
246				softa->ipf_auth_pkts[i] = NULL;
247			}
248		}
249		KFREES(softa->ipf_auth_pkts,
250		       softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts));
251		softa->ipf_auth_pkts = NULL;
252	}
253
254	faep = &softa->ipf_auth_entries;
255	while ((fae = *faep) != NULL) {
256		*faep = fae->fae_next;
257		KFREE(fae);
258	}
259	softa->ipf_auth_ip = NULL;
260
261	if (softa->ipf_auth_rules != NULL) {
262		for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) {
263			if (fr->fr_ref == 1) {
264				*frp = fr->fr_next;
265				MUTEX_DESTROY(&fr->fr_lock);
266				KFREE(fr);
267			} else
268				frp = &fr->fr_next;
269		}
270	}
271
272	return (0);
273}
274
275
276/* ------------------------------------------------------------------------ */
277/* Function:    ipf_auth_soft_destroy                                       */
278/* Returns:     void                                                        */
279/* Parameters:  softc(I) - pointer to soft context data                     */
280/*              arg(I)   - opaque pointer to auth context data              */
281/*                                                                          */
282/* Undo what was done in _create() - i.e. free the soft context data.       */
283/* ------------------------------------------------------------------------ */
284void
285ipf_auth_soft_destroy(ipf_main_softc_t *softc, void *arg)
286{
287	ipf_auth_softc_t *softa = arg;
288
289#if SOLARIS && defined(_KERNEL)
290	cv_destroy(&softa->ipf_auth_wait);
291#endif
292	MUTEX_DESTROY(&softa->ipf_auth_mx);
293	RW_DESTROY(&softa->ipf_authlk);
294
295	KFREE(softa);
296}
297
298
299/* ------------------------------------------------------------------------ */
300/* Function:    ipf_auth_setlock                                            */
301/* Returns:     void                                                        */
302/* Paramters:   arg(I) - pointer to soft context data                       */
303/*              tmp(I) - value to assign to auth lock                       */
304/*                                                                          */
305/* ------------------------------------------------------------------------ */
306void
307ipf_auth_setlock(void *arg, int tmp)
308{
309	ipf_auth_softc_t *softa = arg;
310
311	softa->ipf_auth_lock = tmp;
312}
313
314
315/* ------------------------------------------------------------------------ */
316/* Function:    ipf_auth_check                                              */
317/* Returns:     frentry_t* - pointer to ipf rule if match found, else NULL  */
318/* Parameters:  fin(I)   - pointer to ipftoken structure                    */
319/*              passp(I) - pointer to ipfgeniter structure                  */
320/*                                                                          */
321/* Check if a packet has authorization.  If the packet is found to match an */
322/* authorization result and that would result in a feedback loop (i.e. it   */
323/* will end up returning FR_AUTH) then return FR_BLOCK instead.             */
324/* ------------------------------------------------------------------------ */
325frentry_t *
326ipf_auth_check(fr_info_t *fin, u_32_t *passp)
327{
328	ipf_main_softc_t *softc = fin->fin_main_soft;
329	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
330	frentry_t *fr;
331	frauth_t *fra;
332	u_32_t pass;
333	u_short id;
334	ip_t *ip;
335	int i;
336
337	if (softa->ipf_auth_lock || !softa->ipf_auth_used)
338		return (NULL);
339
340	ip = fin->fin_ip;
341	id = ip->ip_id;
342
343	READ_ENTER(&softa->ipf_authlk);
344	for (i = softa->ipf_auth_start; i != softa->ipf_auth_end; ) {
345		/*
346		 * index becomes -2 only after an SIOCAUTHW.  Check this in
347		 * case the same packet gets sent again and it hasn't yet been
348		 * auth'd.
349		 */
350		fra = softa->ipf_auth + i;
351		if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
352		    !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
353			/*
354			 * Avoid feedback loop.
355			 */
356			if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) {
357				pass = FR_BLOCK;
358				fin->fin_reason = FRB_AUTHFEEDBACK;
359			}
360			/*
361			 * Create a dummy rule for the stateful checking to
362			 * use and return.  Zero out any values we don't
363			 * trust from userland!
364			 */
365			if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
366			     (fin->fin_flx & FI_FRAG))) {
367				KMALLOC(fr, frentry_t *);
368				if (fr) {
369					bcopy((char *)fra->fra_info.fin_fr,
370					      (char *)fr, sizeof(*fr));
371					fr->fr_grp = NULL;
372					fr->fr_ifa = fin->fin_ifp;
373					fr->fr_func = NULL;
374					fr->fr_ref = 1;
375					fr->fr_flags = pass;
376					fr->fr_ifas[1] = NULL;
377					fr->fr_ifas[2] = NULL;
378					fr->fr_ifas[3] = NULL;
379					MUTEX_INIT(&fr->fr_lock,
380						   "ipf auth rule");
381				}
382			} else
383				fr = fra->fra_info.fin_fr;
384			fin->fin_fr = fr;
385			fin->fin_flx |= fra->fra_flx;
386			RWLOCK_EXIT(&softa->ipf_authlk);
387
388			WRITE_ENTER(&softa->ipf_authlk);
389			/*
390			 * ipf_auth_rules is populated with the rules malloc'd
391			 * above and only those.
392			 */
393			if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
394				fr->fr_next = softa->ipf_auth_rules;
395				softa->ipf_auth_rules = fr;
396			}
397			softa->ipf_auth_stats.fas_hits++;
398			fra->fra_index = -1;
399			softa->ipf_auth_used--;
400			softa->ipf_auth_replies--;
401			if (i == softa->ipf_auth_start) {
402				while (fra->fra_index == -1) {
403					i++;
404					fra++;
405					if (i == softa->ipf_auth_size) {
406						i = 0;
407						fra = softa->ipf_auth;
408					}
409					softa->ipf_auth_start = i;
410					if (i == softa->ipf_auth_end)
411						break;
412				}
413				if (softa->ipf_auth_start ==
414				    softa->ipf_auth_end) {
415					softa->ipf_auth_next = 0;
416					softa->ipf_auth_start = 0;
417					softa->ipf_auth_end = 0;
418				}
419			}
420			RWLOCK_EXIT(&softa->ipf_authlk);
421			if (passp != NULL)
422				*passp = pass;
423			softa->ipf_auth_stats.fas_hits++;
424			return (fr);
425		}
426		i++;
427		if (i == softa->ipf_auth_size)
428			i = 0;
429	}
430	RWLOCK_EXIT(&softa->ipf_authlk);
431	softa->ipf_auth_stats.fas_miss++;
432	return (NULL);
433}
434
435
436/* ------------------------------------------------------------------------ */
437/* Function:    ipf_auth_new                                                */
438/* Returns:     int - 1 == success, 0 = did not put packet on auth queue    */
439/* Parameters:  m(I)   - pointer to mb_t with packet in it                  */
440/*              fin(I) - pointer to packet information                      */
441/*                                                                          */
442/* Check if we have room in the auth array to hold details for another      */
443/* packet. If we do, store it and wake up any user programs which are       */
444/* waiting to hear about these events.                                      */
445/* ------------------------------------------------------------------------ */
446int
447ipf_auth_new(mb_t *m, fr_info_t *fin)
448{
449	ipf_main_softc_t *softc = fin->fin_main_soft;
450	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
451#if defined(_KERNEL) && SOLARIS
452	qpktinfo_t *qpi = fin->fin_qpi;
453#endif
454	frauth_t *fra;
455#if !defined(sparc) && !defined(m68k)
456	ip_t *ip;
457#endif
458	int i;
459
460	if (softa->ipf_auth_lock)
461		return (0);
462
463	WRITE_ENTER(&softa->ipf_authlk);
464	if (((softa->ipf_auth_end + 1) % softa->ipf_auth_size) ==
465	    softa->ipf_auth_start) {
466		softa->ipf_auth_stats.fas_nospace++;
467		RWLOCK_EXIT(&softa->ipf_authlk);
468		return (0);
469	}
470
471	softa->ipf_auth_stats.fas_added++;
472	softa->ipf_auth_used++;
473	i = softa->ipf_auth_end++;
474	if (softa->ipf_auth_end == softa->ipf_auth_size)
475		softa->ipf_auth_end = 0;
476
477	fra = softa->ipf_auth + i;
478	fra->fra_index = i;
479	if (fin->fin_fr != NULL)
480		fra->fra_pass = fin->fin_fr->fr_flags;
481	else
482		fra->fra_pass = 0;
483	fra->fra_age = softa->ipf_auth_defaultage;
484	bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
485	fra->fra_flx = fra->fra_info.fin_flx & (FI_STATE|FI_NATED);
486	fra->fra_info.fin_flx &= ~(FI_STATE|FI_NATED);
487#if !defined(sparc) && !defined(m68k)
488	/*
489	 * No need to copyback here as we want to undo the changes, not keep
490	 * them.
491	 */
492	ip = fin->fin_ip;
493# if SOLARIS && defined(_KERNEL)
494	if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
495# endif
496	{
497		register u_short bo;
498
499		bo = ip->ip_len;
500		ip->ip_len = htons(bo);
501		bo = ip->ip_off;
502		ip->ip_off = htons(bo);
503	}
504#endif
505#if SOLARIS && defined(_KERNEL)
506	COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname);
507	m->b_rptr -= qpi->qpi_off;
508	fra->fra_q = qpi->qpi_q;	/* The queue can disappear! */
509	fra->fra_m = *fin->fin_mp;
510	fra->fra_info.fin_mp = &fra->fra_m;
511	softa->ipf_auth_pkts[i] = *(mblk_t **)fin->fin_mp;
512	RWLOCK_EXIT(&softa->ipf_authlk);
513	cv_signal(&softa->ipf_auth_wait);
514	pollwakeup(&softc->ipf_poll_head[IPL_LOGAUTH], POLLIN|POLLRDNORM);
515#else
516	softa->ipf_auth_pkts[i] = m;
517	RWLOCK_EXIT(&softa->ipf_authlk);
518	WAKEUP(&softa->ipf_auth_next, 0);
519#endif
520	return (1);
521}
522
523
524/* ------------------------------------------------------------------------ */
525/* Function:    ipf_auth_ioctl                                              */
526/* Returns:     int - 0 == success, else error                              */
527/* Parameters:  data(IO) - pointer to ioctl data                            */
528/*              cmd(I)   - ioctl command                                    */
529/*              mode(I)  - mode flags associated with open descriptor       */
530/*              uid(I)   - uid associatd with application making the call   */
531/*              ctx(I)   - pointer for context                              */
532/*                                                                          */
533/* This function handles all of the ioctls recognised by the auth component */
534/* in IPFilter - ie ioctls called on an open fd for /dev/ipf_auth           */
535/* ------------------------------------------------------------------------ */
536int
537ipf_auth_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd,
538	int mode, int uid, void *ctx)
539{
540	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
541	int error = 0, i;
542	SPL_INT(s);
543
544	switch (cmd)
545	{
546	case SIOCGENITER :
547	    {
548		ipftoken_t *token;
549		ipfgeniter_t iter;
550		ipfobj_t obj;
551
552		error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER);
553		if (error != 0)
554			break;
555
556		SPL_SCHED(s);
557		token = ipf_token_find(softc, IPFGENITER_AUTH, uid, ctx);
558		if (token != NULL)
559			error = ipf_auth_geniter(softc, token, &iter, &obj);
560		else {
561			WRITE_ENTER(&softc->ipf_tokens);
562			ipf_token_deref(softc, token);
563			RWLOCK_EXIT(&softc->ipf_tokens);
564			IPFERROR(10001);
565			error = ESRCH;
566		}
567		SPL_X(s);
568
569		break;
570	    }
571
572	case SIOCADAFR :
573	case SIOCRMAFR :
574		if (!(mode & FWRITE)) {
575			IPFERROR(10002);
576			error = EPERM;
577		} else
578			error = frrequest(softc, IPL_LOGAUTH, cmd, data,
579					  softc->ipf_active, 1);
580		break;
581
582	case SIOCSTLCK :
583		if (!(mode & FWRITE)) {
584			IPFERROR(10003);
585			error = EPERM;
586		} else {
587			error = ipf_lock(data, &softa->ipf_auth_lock);
588		}
589		break;
590
591	case SIOCATHST:
592		softa->ipf_auth_stats.fas_faelist = softa->ipf_auth_entries;
593		error = ipf_outobj(softc, data, &softa->ipf_auth_stats,
594				   IPFOBJ_AUTHSTAT);
595		break;
596
597	case SIOCIPFFL:
598		SPL_NET(s);
599		WRITE_ENTER(&softa->ipf_authlk);
600		i = ipf_auth_flush(softa);
601		RWLOCK_EXIT(&softa->ipf_authlk);
602		SPL_X(s);
603		error = BCOPYOUT(&i, data, sizeof(i));
604		if (error != 0) {
605			IPFERROR(10004);
606			error = EFAULT;
607		}
608		break;
609
610	case SIOCAUTHW:
611		error = ipf_auth_wait(softc, softa, data);
612		break;
613
614	case SIOCAUTHR:
615		error = ipf_auth_reply(softc, softa, data);
616		break;
617
618	default :
619		IPFERROR(10005);
620		error = EINVAL;
621		break;
622	}
623	return (error);
624}
625
626
627/* ------------------------------------------------------------------------ */
628/* Function:    ipf_auth_expire                                             */
629/* Returns:     None                                                        */
630/* Parameters:  None                                                        */
631/*                                                                          */
632/* Slowly expire held auth records.  Timeouts are set in expectation of     */
633/* this being called twice per second.                                      */
634/* ------------------------------------------------------------------------ */
635void
636ipf_auth_expire(ipf_main_softc_t *softc)
637{
638	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
639	frauthent_t *fae, **faep;
640	frentry_t *fr, **frp;
641	frauth_t *fra;
642	mb_t *m;
643	int i;
644	SPL_INT(s);
645
646	if (softa->ipf_auth_lock)
647		return;
648	SPL_NET(s);
649	WRITE_ENTER(&softa->ipf_authlk);
650	for (i = 0, fra = softa->ipf_auth; i < softa->ipf_auth_size;
651	     i++, fra++) {
652		fra->fra_age--;
653		if ((fra->fra_age == 0) &&
654		    (softa->ipf_auth[i].fra_index != -1)) {
655			if ((m = softa->ipf_auth_pkts[i]) != NULL) {
656				FREE_MB_T(m);
657				softa->ipf_auth_pkts[i] = NULL;
658			} else if (softa->ipf_auth[i].fra_index == -2) {
659				softa->ipf_auth_replies--;
660			}
661			softa->ipf_auth[i].fra_index = -1;
662			softa->ipf_auth_stats.fas_expire++;
663			softa->ipf_auth_used--;
664		}
665	}
666
667	/*
668	 * Expire pre-auth rules
669	 */
670	for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) {
671		fae->fae_age--;
672		if (fae->fae_age == 0) {
673			ipf_auth_deref(&fae);
674			softa->ipf_auth_stats.fas_expire++;
675		} else
676			faep = &fae->fae_next;
677	}
678	if (softa->ipf_auth_entries != NULL)
679		softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr;
680	else
681		softa->ipf_auth_ip = NULL;
682
683	for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) {
684		if (fr->fr_ref == 1) {
685			*frp = fr->fr_next;
686			MUTEX_DESTROY(&fr->fr_lock);
687			KFREE(fr);
688		} else
689			frp = &fr->fr_next;
690	}
691	RWLOCK_EXIT(&softa->ipf_authlk);
692	SPL_X(s);
693}
694
695
696/* ------------------------------------------------------------------------ */
697/* Function:    ipf_auth_precmd                                             */
698/* Returns:     int - 0 == success, else error                              */
699/* Parameters:  cmd(I)  - ioctl command for rule                            */
700/*              fr(I)   - pointer to ipf rule                               */
701/*              fptr(I) - pointer to caller's 'fr'                          */
702/*                                                                          */
703/* ------------------------------------------------------------------------ */
704int
705ipf_auth_precmd(ipf_main_softc_t *softc, ioctlcmd_t cmd, frentry_t *fr,
706	frentry_t **frptr)
707{
708	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
709	frauthent_t *fae, **faep;
710	int error = 0;
711	SPL_INT(s);
712
713	if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) {
714		IPFERROR(10006);
715		return (EIO);
716	}
717
718	for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) {
719		if (&fae->fae_fr == fr)
720			break;
721		else
722			faep = &fae->fae_next;
723	}
724
725	if (cmd == (ioctlcmd_t)SIOCRMAFR) {
726		if (fr == NULL || frptr == NULL) {
727			IPFERROR(10007);
728			error = EINVAL;
729
730		} else if (fae == NULL) {
731			IPFERROR(10008);
732			error = ESRCH;
733
734		} else {
735			SPL_NET(s);
736			WRITE_ENTER(&softa->ipf_authlk);
737			*faep = fae->fae_next;
738			if (softa->ipf_auth_ip == &fae->fae_fr)
739				softa->ipf_auth_ip = softa->ipf_auth_entries ?
740				    &softa->ipf_auth_entries->fae_fr : NULL;
741			RWLOCK_EXIT(&softa->ipf_authlk);
742			SPL_X(s);
743
744			KFREE(fae);
745		}
746	} else if (fr != NULL && frptr != NULL) {
747		KMALLOC(fae, frauthent_t *);
748		if (fae != NULL) {
749			bcopy((char *)fr, (char *)&fae->fae_fr,
750			      sizeof(*fr));
751			SPL_NET(s);
752			WRITE_ENTER(&softa->ipf_authlk);
753			fae->fae_age = softa->ipf_auth_defaultage;
754			fae->fae_fr.fr_hits = 0;
755			fae->fae_fr.fr_next = *frptr;
756			fae->fae_ref = 1;
757			*frptr = &fae->fae_fr;
758			fae->fae_next = *faep;
759			*faep = fae;
760			softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr;
761			RWLOCK_EXIT(&softa->ipf_authlk);
762			SPL_X(s);
763		} else {
764			IPFERROR(10009);
765			error = ENOMEM;
766		}
767	} else {
768		IPFERROR(10010);
769		error = EINVAL;
770	}
771	return (error);
772}
773
774
775/* ------------------------------------------------------------------------ */
776/* Function:    ipf_auth_flush                                              */
777/* Returns:     int - number of auth entries flushed                        */
778/* Parameters:  None                                                        */
779/* Locks:       WRITE(ipf_authlk)                                           */
780/*                                                                          */
781/* This function flushs the ipf_auth_pkts array of any packet data with     */
782/* references still there.                                                  */
783/* It is expected that the caller has already acquired the correct locks or */
784/* set the priority level correctly for this to block out other code paths  */
785/* into these data structures.                                              */
786/* ------------------------------------------------------------------------ */
787static int
788ipf_auth_flush(void *arg)
789{
790	ipf_auth_softc_t *softa = arg;
791	int i, num_flushed;
792	mb_t *m;
793
794	if (softa->ipf_auth_lock)
795		return (-1);
796
797	num_flushed = 0;
798
799	for (i = 0 ; i < softa->ipf_auth_size; i++) {
800		if (softa->ipf_auth[i].fra_index != -1) {
801			m = softa->ipf_auth_pkts[i];
802			if (m != NULL) {
803				FREE_MB_T(m);
804				softa->ipf_auth_pkts[i] = NULL;
805			}
806
807			softa->ipf_auth[i].fra_index = -1;
808			/* perhaps add & use a flush counter inst.*/
809			softa->ipf_auth_stats.fas_expire++;
810			num_flushed++;
811		}
812	}
813
814	softa->ipf_auth_start = 0;
815	softa->ipf_auth_end = 0;
816	softa->ipf_auth_next = 0;
817	softa->ipf_auth_used = 0;
818	softa->ipf_auth_replies = 0;
819
820	return (num_flushed);
821}
822
823
824/* ------------------------------------------------------------------------ */
825/* Function:    ipf_auth_waiting                                            */
826/* Returns:     int - number of packets in the auth queue                   */
827/* Parameters:  None                                                        */
828/*                                                                          */
829/* Simple truth check to see if there are any packets waiting in the auth   */
830/* queue.                                                                   */
831/* ------------------------------------------------------------------------ */
832int
833ipf_auth_waiting(ipf_main_softc_t *softc)
834{
835	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
836
837	return (softa->ipf_auth_used != 0);
838}
839
840
841/* ------------------------------------------------------------------------ */
842/* Function:    ipf_auth_geniter                                            */
843/* Returns:     int - 0 == success, else error                              */
844/* Parameters:  token(I) - pointer to ipftoken structure                    */
845/*              itp(I)   - pointer to ipfgeniter structure                  */
846/*              objp(I)  - pointer to ipf object destription                */
847/*                                                                          */
848/* Iterate through the list of entries in the auth queue list.              */
849/* objp is used here to get the location of where to do the copy out to.    */
850/* Stomping over various fields with new information will not harm anything */
851/* ------------------------------------------------------------------------ */
852static int
853ipf_auth_geniter(ipf_main_softc_t *softc, ipftoken_t *token,
854	ipfgeniter_t *itp, ipfobj_t *objp)
855{
856	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
857	frauthent_t *fae, *next, zero;
858	int error;
859
860	if (itp->igi_data == NULL) {
861		IPFERROR(10011);
862		return (EFAULT);
863	}
864
865	if (itp->igi_type != IPFGENITER_AUTH) {
866		IPFERROR(10012);
867		return (EINVAL);
868	}
869
870	objp->ipfo_type = IPFOBJ_FRAUTH;
871	objp->ipfo_ptr = itp->igi_data;
872	objp->ipfo_size = sizeof(frauth_t);
873
874	READ_ENTER(&softa->ipf_authlk);
875
876	fae = token->ipt_data;
877	if (fae == NULL) {
878		next = softa->ipf_auth_entries;
879	} else {
880		next = fae->fae_next;
881	}
882
883	/*
884	 * If we found an auth entry to use, bump its reference count
885	 * so that it can be used for is_next when we come back.
886	 */
887	if (next != NULL) {
888		ATOMIC_INC(next->fae_ref);
889		token->ipt_data = next;
890	} else {
891		bzero(&zero, sizeof(zero));
892		next = &zero;
893		token->ipt_data = NULL;
894	}
895
896	RWLOCK_EXIT(&softa->ipf_authlk);
897
898	error = ipf_outobjk(softc, objp, next);
899	if (fae != NULL)
900		ipf_auth_deref_unlocked(softa, &fae);
901
902	if (next->fae_next == NULL)
903		ipf_token_mark_complete(token);
904	return (error);
905}
906
907
908/* ------------------------------------------------------------------------ */
909/* Function:    ipf_auth_deref_unlocked                                     */
910/* Returns:     None                                                        */
911/* Parameters:  faep(IO) - pointer to caller's frauthent_t pointer          */
912/*                                                                          */
913/* Wrapper for ipf_auth_deref for when a write lock on ipf_authlk is not    */
914/* held.                                                                    */
915/* ------------------------------------------------------------------------ */
916static void
917ipf_auth_deref_unlocked(ipf_auth_softc_t *softa, frauthent_t **faep)
918{
919	WRITE_ENTER(&softa->ipf_authlk);
920	ipf_auth_deref(faep);
921	RWLOCK_EXIT(&softa->ipf_authlk);
922}
923
924
925/* ------------------------------------------------------------------------ */
926/* Function:    ipf_auth_deref                                              */
927/* Returns:     None                                                        */
928/* Parameters:  faep(IO) - pointer to caller's frauthent_t pointer          */
929/* Locks:       WRITE(ipf_authlk)                                           */
930/*                                                                          */
931/* This function unconditionally sets the pointer in the caller to NULL,    */
932/* to make it clear that it should no longer use that pointer, and drops    */
933/* the reference count on the structure by 1.  If it reaches 0, free it up. */
934/* ------------------------------------------------------------------------ */
935static void
936ipf_auth_deref(frauthent_t **faep)
937{
938	frauthent_t *fae;
939
940	fae = *faep;
941	*faep = NULL;
942
943	fae->fae_ref--;
944	if (fae->fae_ref == 0) {
945		KFREE(fae);
946	}
947}
948
949
950/* ------------------------------------------------------------------------ */
951/* Function:    ipf_auth_wait_pkt                                           */
952/* Returns:     int - 0 == success, else error                              */
953/* Parameters:  data(I) - pointer to data from ioctl call                   */
954/*                                                                          */
955/* This function is called when an application is waiting for a packet to   */
956/* match an "auth" rule by issuing an SIOCAUTHW ioctl.  If there is already */
957/* a packet waiting on the queue then we will return that _one_ immediately.*/
958/* If there are no packets present in the queue (ipf_auth_pkts) then we go  */
959/* to sleep.                                                                */
960/* ------------------------------------------------------------------------ */
961static int
962ipf_auth_wait(ipf_main_softc_t *softc, ipf_auth_softc_t *softa, char *data)
963{
964	frauth_t auth, *au = &auth;
965	int error, len, i;
966	mb_t *m;
967	char *t;
968	SPL_INT(s);
969
970ipf_auth_ioctlloop:
971	error = ipf_inobj(softc, data, NULL, au, IPFOBJ_FRAUTH);
972	if (error != 0)
973		return (error);
974
975	/*
976	 * XXX Locks are held below over calls to copyout...a better
977	 * solution needs to be found so this isn't necessary.  The situation
978	 * we are trying to guard against here is an error in the copyout
979	 * steps should not cause the packet to "disappear" from the queue.
980	 */
981	SPL_NET(s);
982	READ_ENTER(&softa->ipf_authlk);
983
984	/*
985	 * If ipf_auth_next is not equal to ipf_auth_end it will be because
986	 * there is a packet waiting to be delt with in the ipf_auth_pkts
987	 * array.  We copy as much of that out to user space as requested.
988	 */
989	if (softa->ipf_auth_used > 0) {
990		while (softa->ipf_auth_pkts[softa->ipf_auth_next] == NULL) {
991			softa->ipf_auth_next++;
992			if (softa->ipf_auth_next == softa->ipf_auth_size)
993				softa->ipf_auth_next = 0;
994		}
995
996		error = ipf_outobj(softc, data,
997				   &softa->ipf_auth[softa->ipf_auth_next],
998				   IPFOBJ_FRAUTH);
999		if (error != 0) {
1000			RWLOCK_EXIT(&softa->ipf_authlk);
1001			SPL_X(s);
1002			return (error);
1003		}
1004
1005		if (auth.fra_len != 0 && auth.fra_buf != NULL) {
1006			/*
1007			 * Copy packet contents out to user space if
1008			 * requested.  Bail on an error.
1009			 */
1010			m = softa->ipf_auth_pkts[softa->ipf_auth_next];
1011			len = MSGDSIZE(m);
1012			if (len > auth.fra_len)
1013				len = auth.fra_len;
1014			auth.fra_len = len;
1015
1016			for (t = auth.fra_buf; m && (len > 0); ) {
1017				i = MIN(M_LEN(m), len);
1018				error = copyoutptr(softc, MTOD(m, char *),
1019						   &t, i);
1020				len -= i;
1021				t += i;
1022				if (error != 0) {
1023					RWLOCK_EXIT(&softa->ipf_authlk);
1024					SPL_X(s);
1025					return (error);
1026				}
1027				m = m->m_next;
1028			}
1029		}
1030		RWLOCK_EXIT(&softa->ipf_authlk);
1031
1032		SPL_NET(s);
1033		WRITE_ENTER(&softa->ipf_authlk);
1034		softa->ipf_auth_next++;
1035		if (softa->ipf_auth_next == softa->ipf_auth_size)
1036			softa->ipf_auth_next = 0;
1037		RWLOCK_EXIT(&softa->ipf_authlk);
1038		SPL_X(s);
1039
1040		return (0);
1041	}
1042	RWLOCK_EXIT(&softa->ipf_authlk);
1043	SPL_X(s);
1044
1045	MUTEX_ENTER(&softa->ipf_auth_mx);
1046#ifdef	_KERNEL
1047# if	SOLARIS
1048	error = 0;
1049	if (!cv_wait_sig(&softa->ipf_auth_wait, &softa->ipf_auth_mx.ipf_lk)) {
1050		IPFERROR(10014);
1051		error = EINTR;
1052	}
1053# else /* SOLARIS */
1054	error = SLEEP(&softa->ipf_auth_next, "ipf_auth_next");
1055# endif /* SOLARIS */
1056#endif
1057	MUTEX_EXIT(&softa->ipf_auth_mx);
1058	if (error == 0)
1059		goto ipf_auth_ioctlloop;
1060	return (error);
1061}
1062
1063
1064/* ------------------------------------------------------------------------ */
1065/* Function:    ipf_auth_reply                                              */
1066/* Returns:     int - 0 == success, else error                              */
1067/* Parameters:  data(I) - pointer to data from ioctl call                   */
1068/*                                                                          */
1069/* This function is called by an application when it wants to return a      */
1070/* decision on a packet using the SIOCAUTHR ioctl.  This is after it has    */
1071/* received information using an SIOCAUTHW.  The decision returned in the   */
1072/* form of flags, the same as those used in each rule.                      */
1073/* ------------------------------------------------------------------------ */
1074static int
1075ipf_auth_reply(ipf_main_softc_t *softc, ipf_auth_softc_t *softa, char *data)
1076{
1077	frauth_t auth, *au = &auth, *fra;
1078	fr_info_t fin;
1079	int error, i;
1080	mb_t *m;
1081	SPL_INT(s);
1082
1083	error = ipf_inobj(softc, data, NULL, &auth, IPFOBJ_FRAUTH);
1084	if (error != 0)
1085		return (error);
1086
1087	SPL_NET(s);
1088	WRITE_ENTER(&softa->ipf_authlk);
1089
1090	i = au->fra_index;
1091	fra = softa->ipf_auth + i;
1092	error = 0;
1093
1094	/*
1095	 * Check the validity of the information being returned with two simple
1096	 * checks.  First, the auth index value should be within the size of
1097	 * the array and second the packet id being returned should also match.
1098	 */
1099	if ((i < 0) || (i >= softa->ipf_auth_size)) {
1100		RWLOCK_EXIT(&softa->ipf_authlk);
1101		SPL_X(s);
1102		IPFERROR(10015);
1103		return (ESRCH);
1104	}
1105	if  (fra->fra_info.fin_id != au->fra_info.fin_id) {
1106		RWLOCK_EXIT(&softa->ipf_authlk);
1107		SPL_X(s);
1108		IPFERROR(10019);
1109		return (ESRCH);
1110	}
1111
1112	m = softa->ipf_auth_pkts[i];
1113	fra->fra_index = -2;
1114	fra->fra_pass = au->fra_pass;
1115	softa->ipf_auth_pkts[i] = NULL;
1116	softa->ipf_auth_replies++;
1117	bcopy(&fra->fra_info, &fin, sizeof(fin));
1118
1119	RWLOCK_EXIT(&softa->ipf_authlk);
1120
1121	/*
1122	 * Re-insert the packet back into the packet stream flowing through
1123	 * the kernel in a manner that will mean IPFilter sees the packet
1124	 * again.  This is not the same as is done with fastroute,
1125	 * deliberately, as we want to resume the normal packet processing
1126	 * path for it.
1127	 */
1128#ifdef	_KERNEL
1129	if ((m != NULL) && (au->fra_info.fin_out != 0)) {
1130		error = ipf_inject(&fin, m);
1131		if (error != 0) {
1132			IPFERROR(10016);
1133			error = ENOBUFS;
1134			softa->ipf_auth_stats.fas_sendfail++;
1135		} else {
1136			softa->ipf_auth_stats.fas_sendok++;
1137		}
1138	} else if (m) {
1139		error = ipf_inject(&fin, m);
1140		if (error != 0) {
1141			IPFERROR(10017);
1142			error = ENOBUFS;
1143			softa->ipf_auth_stats.fas_quefail++;
1144		} else {
1145			softa->ipf_auth_stats.fas_queok++;
1146		}
1147	} else {
1148		IPFERROR(10018);
1149		error = EINVAL;
1150	}
1151
1152	/*
1153	 * If we experience an error which will result in the packet
1154	 * not being processed, make sure we advance to the next one.
1155	 */
1156	if (error == ENOBUFS) {
1157		WRITE_ENTER(&softa->ipf_authlk);
1158		softa->ipf_auth_used--;
1159		fra->fra_index = -1;
1160		fra->fra_pass = 0;
1161		if (i == softa->ipf_auth_start) {
1162			while (fra->fra_index == -1) {
1163				i++;
1164				if (i == softa->ipf_auth_size)
1165					i = 0;
1166				softa->ipf_auth_start = i;
1167				if (i == softa->ipf_auth_end)
1168					break;
1169			}
1170			if (softa->ipf_auth_start == softa->ipf_auth_end) {
1171				softa->ipf_auth_next = 0;
1172				softa->ipf_auth_start = 0;
1173				softa->ipf_auth_end = 0;
1174			}
1175		}
1176		RWLOCK_EXIT(&softa->ipf_authlk);
1177	}
1178#endif /* _KERNEL */
1179	SPL_X(s);
1180
1181	return (0);
1182}
1183
1184
1185u_32_t
1186ipf_auth_pre_scanlist(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass)
1187{
1188	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
1189
1190	if (softa->ipf_auth_ip != NULL)
1191		return (ipf_scanlist(fin, softc->ipf_pass));
1192
1193	return (pass);
1194}
1195
1196
1197frentry_t **
1198ipf_auth_rulehead(ipf_main_softc_t *softc)
1199{
1200	ipf_auth_softc_t *softa = softc->ipf_auth_soft;
1201
1202	return (&softa->ipf_auth_ip);
1203}
1204