xref: /freebsd/sys/netpfil/ipfilter/netinet/ip_auth.c (revision 3494f7c0)
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 
113 static void ipf_auth_deref(frauthent_t **);
114 static void ipf_auth_deref_unlocked(ipf_auth_softc_t *, frauthent_t **);
115 static int ipf_auth_geniter(ipf_main_softc_t *, ipftoken_t *,
116 				 ipfgeniter_t *, ipfobj_t *);
117 static int ipf_auth_reply(ipf_main_softc_t *, ipf_auth_softc_t *, char *);
118 static int ipf_auth_wait(ipf_main_softc_t *, ipf_auth_softc_t *, char *);
119 static 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 /* ------------------------------------------------------------------------ */
130 int
131 ipf_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 /* ------------------------------------------------------------------------ */
145 int
146 ipf_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 /* ------------------------------------------------------------------------ */
160 void *
161 ipf_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 /* ------------------------------------------------------------------------ */
192 int
193 ipf_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 /* ------------------------------------------------------------------------ */
226 int
227 ipf_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 /* ------------------------------------------------------------------------ */
284 void
285 ipf_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 /* ------------------------------------------------------------------------ */
306 void
307 ipf_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 /* ------------------------------------------------------------------------ */
325 frentry_t *
326 ipf_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 /* ------------------------------------------------------------------------ */
446 int
447 ipf_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 /* ------------------------------------------------------------------------ */
536 int
537 ipf_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 /* ------------------------------------------------------------------------ */
635 void
636 ipf_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 /* ------------------------------------------------------------------------ */
704 int
705 ipf_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 /* ------------------------------------------------------------------------ */
787 static int
788 ipf_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 /* ------------------------------------------------------------------------ */
832 int
833 ipf_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 /* ------------------------------------------------------------------------ */
852 static int
853 ipf_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 /* ------------------------------------------------------------------------ */
916 static void
917 ipf_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 /* ------------------------------------------------------------------------ */
935 static void
936 ipf_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 /* ------------------------------------------------------------------------ */
961 static int
962 ipf_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 
970 ipf_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 /* ------------------------------------------------------------------------ */
1074 static int
1075 ipf_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 
1185 u_32_t
1186 ipf_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 
1197 frentry_t **
1198 ipf_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