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