1/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 183550 2008-10-02 15:37:58Z zec $ */
2
3/*
4 * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij.
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#include <sys/errno.h>
15#include <sys/types.h>
16#include <sys/param.h>
17#include <sys/time.h>
18#include <sys/file.h>
19#if !defined(_KERNEL)
20# include <stdio.h>
21# include <stdlib.h>
22# include <string.h>
23# define _KERNEL
24# ifdef __OpenBSD__
25struct file;
26# endif
27# include <sys/uio.h>
28# undef _KERNEL
29#endif
30#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
31# include <sys/filio.h>
32# include <sys/fcntl.h>
33#else
34# include <sys/ioctl.h>
35#endif
36#if !defined(linux)
37# include <sys/protosw.h>
38#endif
39#include <sys/socket.h>
40#if defined(_KERNEL)
41# include <sys/systm.h>
42# if !defined(__SVR4) && !defined(__svr4__) && !defined(linux)
43# include <sys/mbuf.h>
44# endif
45#endif
46#if defined(__SVR4) || defined(__svr4__)
47# include <sys/filio.h>
48# include <sys/byteorder.h>
49# ifdef _KERNEL
50# include <sys/dditypes.h>
51# endif
52# include <sys/stream.h>
53# include <sys/kmem.h>
54#endif
55#if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \
56 (defined(__FreeBSD_version) &&(__FreeBSD_version >= 400000))
57# include <sys/queue.h>
58#endif
59#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
60# include <machine/cpu.h>
61#endif
62#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
63# include <sys/proc.h>
64#endif
65#include <net/if.h>
66#ifdef sun
67# include <net/af.h>
68#endif
69#include <net/route.h>
70#include <netinet/in.h>
71#include <netinet/in_systm.h>
72#include <netinet/ip.h>
73#if !defined(_KERNEL) && defined(__FreeBSD_version) && \
74 __FreeBSD_version >= 800049
75# define V_ip_do_randomid ip_do_randomid
76# define V_ip_id ip_id
77#endif
78#if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi)
79# define KERNEL
80# define _KERNEL
81# define NOT_KERNEL
82#endif
83#if !defined(linux)
84# include <netinet/ip_var.h>
85#endif
86#ifdef NOT_KERNEL
87# undef _KERNEL
88# undef KERNEL
89#endif
90#include <netinet/tcp.h>
91#if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */
92extern struct ifqueue ipintrq; /* ip packet input queue */
93#else
94# if !defined(__hpux) && !defined(linux)
95# if __FreeBSD_version >= 300000
96# include <net/if_var.h>
97# if __FreeBSD_version >= 500042
98# define IF_QFULL _IF_QFULL
99# define IF_DROP _IF_DROP
100# endif /* __FreeBSD_version >= 500042 */
101# endif
102# include <netinet/in_var.h>
103# include <netinet/tcp_fsm.h>
104# endif
105#endif
106#include <netinet/udp.h>
107#include <netinet/ip_icmp.h>
108#include "netinet/ip_compat.h"
109#include <netinet/tcpip.h>
110#include "netinet/ip_fil.h"
111#include "netinet/ip_auth.h"
112#if !defined(MENTAT) && !defined(linux)
113# include <net/netisr.h>
114# ifdef __FreeBSD__
115# include <machine/cpufunc.h>
116# endif
117#endif
118#if (__FreeBSD_version >= 300000)
119# include <sys/malloc.h>
120# if defined(_KERNEL) && !defined(IPFILTER_LKM)
121# include <sys/libkern.h>
122# include <sys/systm.h>
123# endif
124#endif
125/* END OF INCLUDES */
126
127#if !defined(lint)
128static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 183550 2008-10-02 15:37:58Z zec $";
129/* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.24 2007/09/09 11:32:04 darrenr Exp $"; */
130#endif
131
132
133#if SOLARIS && defined(_KERNEL)
134extern kcondvar_t ipfauthwait;
135extern struct pollhead iplpollhead[IPL_LOGSIZE];
136#endif /* SOLARIS */
137#if defined(linux) && defined(_KERNEL)
138wait_queue_head_t fr_authnext_linux;
139#endif
140
141int fr_authsize = FR_NUMAUTH;
142int fr_authused = 0;
143int fr_defaultauthage = 600;
144int fr_auth_lock = 0;
145int fr_auth_init = 0;
146fr_authstat_t fr_authstats;
147static frauth_t *fr_auth = NULL;
148mb_t **fr_authpkts = NULL;
149int fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
150frauthent_t *fae_list = NULL;
151frentry_t *ipauth = NULL,
152 *fr_authlist = NULL;
153
154void fr_authderef __P((frauthent_t **));
155int fr_authgeniter __P((ipftoken_t *, ipfgeniter_t *));
156int fr_authreply __P((char *));
157int fr_authwait __P((char *));
158
159/* ------------------------------------------------------------------------ */
160/* Function: fr_authinit */
161/* Returns: int - 0 == success, else error */
162/* Parameters: None */
163/* */
164/* Allocate memory and initialise data structures used in handling auth */
165/* rules. */
166/* ------------------------------------------------------------------------ */
167int fr_authinit()
168{
169 KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
170 if (fr_auth != NULL)
171 bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
172 else
173 return -1;
174
175 KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
176 if (fr_authpkts != NULL)
177 bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
178 else
179 return -2;
180
181 MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
182 RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
183#if SOLARIS && defined(_KERNEL)
184 cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
185#endif
186#if defined(linux) && defined(_KERNEL)
187 init_waitqueue_head(&fr_authnext_linux);
188#endif
189
190 fr_auth_init = 1;
191
192 return 0;
193}
194
195
196/* ------------------------------------------------------------------------ */
197/* Function: fr_checkauth */
198/* Returns: frentry_t* - pointer to ipf rule if match found, else NULL */
199/* Parameters: fin(I) - pointer to ipftoken structure */
200/* passp(I) - pointer to ipfgeniter structure */
201/* */
202/* Check if a packet has authorization. If the packet is found to match an */
203/* authorization result and that would result in a feedback loop (i.e. it */
204/* will end up returning FR_AUTH) then return FR_BLOCK instead. */
205/* ------------------------------------------------------------------------ */
206frentry_t *fr_checkauth(fin, passp)
207fr_info_t *fin;
208u_32_t *passp;
209{
210 frentry_t *fr;
211 frauth_t *fra;
212 u_32_t pass;
213 u_short id;
214 ip_t *ip;
215 int i;
216
217 if (fr_auth_lock || !fr_authused)
218 return NULL;
219
220 ip = fin->fin_ip;
221 id = ip->ip_id;
222
223 READ_ENTER(&ipf_auth);
224 for (i = fr_authstart; i != fr_authend; ) {
225 /*
226 * index becomes -2 only after an SIOCAUTHW. Check this in
227 * case the same packet gets sent again and it hasn't yet been
228 * auth'd.
229 */
230 fra = fr_auth + i;
231 if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
232 !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
233 /*
234 * Avoid feedback loop.
235 */
236 if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
237 pass = FR_BLOCK;
238 /*
239 * Create a dummy rule for the stateful checking to
240 * use and return. Zero out any values we don't
241 * trust from userland!
242 */
243 if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
244 (fin->fin_flx & FI_FRAG))) {
245 KMALLOC(fr, frentry_t *);
246 if (fr) {
247 bcopy((char *)fra->fra_info.fin_fr,
248 (char *)fr, sizeof(*fr));
249 fr->fr_grp = NULL;
250 fr->fr_ifa = fin->fin_ifp;
251 fr->fr_func = NULL;
252 fr->fr_ref = 1;
253 fr->fr_flags = pass;
254 fr->fr_ifas[1] = NULL;
255 fr->fr_ifas[2] = NULL;
256 fr->fr_ifas[3] = NULL;
257 }
258 } else
259 fr = fra->fra_info.fin_fr;
260 fin->fin_fr = fr;
261 RWLOCK_EXIT(&ipf_auth);
262
263 WRITE_ENTER(&ipf_auth);
264 /*
265 * fr_authlist is populated with the rules malloc'd
266 * above and only those.
267 */
268 if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
269 fr->fr_next = fr_authlist;
270 fr_authlist = fr;
271 }
272 fr_authstats.fas_hits++;
273 fra->fra_index = -1;
274 fr_authused--;
275 if (i == fr_authstart) {
276 while (fra->fra_index == -1) {
277 i++;
278 fra++;
279 if (i == fr_authsize) {
280 i = 0;
281 fra = fr_auth;
282 }
283 fr_authstart = i;
284 if (i == fr_authend)
285 break;
286 }
287 if (fr_authstart == fr_authend) {
288 fr_authnext = 0;
289 fr_authstart = fr_authend = 0;
290 }
291 }
292 RWLOCK_EXIT(&ipf_auth);
293 if (passp != NULL)
294 *passp = pass;
295 ATOMIC_INC64(fr_authstats.fas_hits);
296 return fr;
297 }
298 i++;
299 if (i == fr_authsize)
300 i = 0;
301 }
302 fr_authstats.fas_miss++;
303 RWLOCK_EXIT(&ipf_auth);
304 ATOMIC_INC64(fr_authstats.fas_miss);
305 return NULL;
306}
307
308
309/* ------------------------------------------------------------------------ */
310/* Function: fr_newauth */
311/* Returns: int - 1 == success, 0 = did not put packet on auth queue */
312/* Parameters: m(I) - pointer to mb_t with packet in it */
313/* fin(I) - pointer to packet information */
314/* */
315/* Check if we have room in the auth array to hold details for another */
316/* packet. If we do, store it and wake up any user programs which are */
317/* waiting to hear about these events. */
318/* ------------------------------------------------------------------------ */
319int fr_newauth(m, fin)
320mb_t *m;
321fr_info_t *fin;
322{
323#if defined(_KERNEL) && defined(MENTAT)
324 qpktinfo_t *qpi = fin->fin_qpi;
325#endif
326 frauth_t *fra;
327#if !defined(sparc) && !defined(m68k)
328 ip_t *ip;
329#endif
330 int i;
331
332 if (fr_auth_lock)
333 return 0;
334
335 WRITE_ENTER(&ipf_auth);
336 if (((fr_authend + 1) % fr_authsize) == fr_authstart) {
337 fr_authstats.fas_nospace++;
338 RWLOCK_EXIT(&ipf_auth);
339 return 0;
340 }
341
342 fr_authstats.fas_added++;
343 fr_authused++;
344 i = fr_authend++;
345 if (fr_authend == fr_authsize)
346 fr_authend = 0;
347 fra = fr_auth + i;
348 fra->fra_index = i;
349 RWLOCK_EXIT(&ipf_auth);
350
351 if (fin->fin_fr != NULL)
352 fra->fra_pass = fin->fin_fr->fr_flags;
353 else
354 fra->fra_pass = 0;
355 fra->fra_age = fr_defaultauthage;
356 bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
357#if !defined(sparc) && !defined(m68k)
358 /*
359 * No need to copyback here as we want to undo the changes, not keep
360 * them.
361 */
362 ip = fin->fin_ip;
363# if defined(MENTAT) && defined(_KERNEL)
364 if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
365# endif
366 {
367 register u_short bo;
368
369 bo = ip->ip_len;
370 ip->ip_len = htons(bo);
371 bo = ip->ip_off;
372 ip->ip_off = htons(bo);
373 }
374#endif
375#if SOLARIS && defined(_KERNEL)
376 COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname);
377 m->b_rptr -= qpi->qpi_off;
378 fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
379# if !defined(_INET_IP_STACK_H)
380 fra->fra_q = qpi->qpi_q; /* The queue can disappear! */
381# endif
382 fra->fra_m = *fin->fin_mp;
383 fra->fra_info.fin_mp = &fra->fra_m;
384 cv_signal(&ipfauthwait);
385 pollwakeup(&iplpollhead[IPL_LOGAUTH], POLLIN|POLLRDNORM);
386#else
387 fr_authpkts[i] = m;
388 WAKEUP(&fr_authnext,0);
389#endif
390 return 1;
391}
392
393
394/* ------------------------------------------------------------------------ */
395/* Function: fr_auth_ioctl */
396/* Returns: int - 0 == success, else error */
397/* Parameters: data(IO) - pointer to ioctl data */
398/* cmd(I) - ioctl command */
399/* mode(I) - mode flags associated with open descriptor */
400/* uid(I) - uid associatd with application making the call */
401/* ctx(I) - pointer for context */
402/* */
403/* This function handles all of the ioctls recognised by the auth component */
404/* in IPFilter - ie ioctls called on an open fd for /dev/ipauth */
405/* ------------------------------------------------------------------------ */
406int fr_auth_ioctl(data, cmd, mode, uid, ctx)
407caddr_t data;
408ioctlcmd_t cmd;
409int mode, uid;
410void *ctx;
411{
412 int error = 0, i;
413 SPL_INT(s);
414
415 switch (cmd)
416 {
417 case SIOCGENITER :
418 {
419 ipftoken_t *token;
420 ipfgeniter_t iter;
421
422 error = fr_inobj(data, &iter, IPFOBJ_GENITER);
423 if (error != 0)
424 break;
425
426 SPL_SCHED(s);
427 token = ipf_findtoken(IPFGENITER_AUTH, uid, ctx);
428 if (token != NULL)
429 error = fr_authgeniter(token, &iter);
430 else
431 error = ESRCH;
432 RWLOCK_EXIT(&ipf_tokens);
433 SPL_X(s);
434
435 break;
436 }
437
438 case SIOCADAFR :
439 case SIOCRMAFR :
440 if (!(mode & FWRITE))
441 error = EPERM;
442 else
443 error = frrequest(IPL_LOGAUTH, cmd, data,
444 fr_active, 1);
445 break;
446
447 case SIOCSTLCK :
448 if (!(mode & FWRITE)) {
449 error = EPERM;
450 break;
451 }
452 error = fr_lock(data, &fr_auth_lock);
453 break;
454
455 case SIOCATHST:
456 fr_authstats.fas_faelist = fae_list;
457 error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
458 break;
459
460 case SIOCIPFFL:
461 SPL_NET(s);
462 WRITE_ENTER(&ipf_auth);
463 i = fr_authflush();
464 RWLOCK_EXIT(&ipf_auth);
465 SPL_X(s);
466 error = BCOPYOUT((char *)&i, data, sizeof(i));
467 if (error != 0)
468 error = EFAULT;
469 break;
470
471 case SIOCAUTHW:
472 error = fr_authwait(data);
473 break;
474
475 case SIOCAUTHR:
476 error = fr_authreply(data);
477 break;
478
479 default :
480 error = EINVAL;
481 break;
482 }
483 return error;
484}
485
486
487/* ------------------------------------------------------------------------ */
488/* Function: fr_authunload */
489/* Returns: None */
490/* Parameters: None */
491/* */
492/* Free all network buffer memory used to keep saved packets. */
493/* ------------------------------------------------------------------------ */
494void fr_authunload()
495{
496 register int i;
497 register frauthent_t *fae, **faep;
498 frentry_t *fr, **frp;
499 mb_t *m;
500
501 if (fr_auth != NULL) {
502 KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
503 fr_auth = NULL;
504 }
505
506 if (fr_authpkts != NULL) {
507 for (i = 0; i < fr_authsize; i++) {
508 m = fr_authpkts[i];
509 if (m != NULL) {
510 FREE_MB_T(m);
511 fr_authpkts[i] = NULL;
512 }
513 }
514 KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
515 fr_authpkts = NULL;
516 }
517
518 faep = &fae_list;
519 while ((fae = *faep) != NULL) {
520 *faep = fae->fae_next;
521 KFREE(fae);
522 }
523 ipauth = NULL;
524
525 if (fr_authlist != NULL) {
526 for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
527 if (fr->fr_ref == 1) {
528 *frp = fr->fr_next;
529 KFREE(fr);
530 } else
531 frp = &fr->fr_next;
532 }
533 }
534
535 if (fr_auth_init == 1) {
536# if SOLARIS && defined(_KERNEL)
537 cv_destroy(&ipfauthwait);
538# endif
539 MUTEX_DESTROY(&ipf_authmx);
540 RW_DESTROY(&ipf_auth);
541
542 fr_auth_init = 0;
543 }
544}
545
546
547/* ------------------------------------------------------------------------ */
548/* Function: fr_authexpire */
549/* Returns: None */
550/* Parameters: None */
551/* */
552/* Slowly expire held auth records. Timeouts are set in expectation of */
553/* this being called twice per second. */
554/* ------------------------------------------------------------------------ */
555void fr_authexpire()
556{
557 frauthent_t *fae, **faep;
558 frentry_t *fr, **frp;
559 frauth_t *fra;
560 mb_t *m;
561 int i;
562 SPL_INT(s);
563
564 if (fr_auth_lock)
565 return;
566
567 SPL_NET(s);
568 WRITE_ENTER(&ipf_auth);
569 for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
570 fra->fra_age--;
571 if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
572 FREE_MB_T(m);
573 fr_authpkts[i] = NULL;
574 fr_auth[i].fra_index = -1;
575 fr_authstats.fas_expire++;
576 fr_authused--;
577 }
578 }
579
580 /*
581 * Expire pre-auth rules
582 */
583 for (faep = &fae_list; ((fae = *faep) != NULL); ) {
584 fae->fae_age--;
585 if (fae->fae_age == 0) {
586 fr_authderef(&fae);
587 fr_authstats.fas_expire++;
588 } else
589 faep = &fae->fae_next;
590 }
591 if (fae_list != NULL)
592 ipauth = &fae_list->fae_fr;
593 else
594 ipauth = NULL;
595
596 for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
597 if (fr->fr_ref == 1) {
598 *frp = fr->fr_next;
599 KFREE(fr);
600 } else
601 frp = &fr->fr_next;
602 }
603 RWLOCK_EXIT(&ipf_auth);
604 SPL_X(s);
605}
606
607
608/* ------------------------------------------------------------------------ */
609/* Function: fr_preauthcmd */
610/* Returns: int - 0 == success, else error */
611/* Parameters: cmd(I) - ioctl command for rule */
612/* fr(I) - pointer to ipf rule */
613/* fptr(I) - pointer to caller's 'fr' */
614/* */
615/* ------------------------------------------------------------------------ */
616int fr_preauthcmd(cmd, fr, frptr)
617ioctlcmd_t cmd;
618frentry_t *fr, **frptr;
619{
620 frauthent_t *fae, **faep;
621 int error = 0;
622 SPL_INT(s);
623
624 if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
625 return EIO;
626
627 for (faep = &fae_list; ((fae = *faep) != NULL); ) {
628 if (&fae->fae_fr == fr)
629 break;
630 else
631 faep = &fae->fae_next;
632 }
633
634 if (cmd == (ioctlcmd_t)SIOCRMAFR) {
635 if (fr == NULL || frptr == NULL)
636 error = EINVAL;
637 else if (fae == NULL)
638 error = ESRCH;
639 else {
640 SPL_NET(s);
641 WRITE_ENTER(&ipf_auth);
642 *faep = fae->fae_next;
643 if (ipauth == &fae->fae_fr)
644 ipauth = fae_list ? &fae_list->fae_fr : NULL;
645 RWLOCK_EXIT(&ipf_auth);
646 SPL_X(s);
647
648 KFREE(fae);
649 }
650 } else if (fr != NULL && frptr != NULL) {
651 KMALLOC(fae, frauthent_t *);
652 if (fae != NULL) {
653 bcopy((char *)fr, (char *)&fae->fae_fr,
654 sizeof(*fr));
655 SPL_NET(s);
656 WRITE_ENTER(&ipf_auth);
657 fae->fae_age = fr_defaultauthage;
658 fae->fae_fr.fr_hits = 0;
659 fae->fae_fr.fr_next = *frptr;
660 fae->fae_ref = 1;
661 *frptr = &fae->fae_fr;
662 fae->fae_next = *faep;
663 *faep = fae;
664 ipauth = &fae_list->fae_fr;
665 RWLOCK_EXIT(&ipf_auth);
666 SPL_X(s);
667 } else
668 error = ENOMEM;
669 } else
670 error = EINVAL;
671 return error;
672}
673
674
675/* ------------------------------------------------------------------------ */
676/* Function: fr_authflush */
677/* Returns: int - number of auth entries flushed */
678/* Parameters: None */
679/* Locks: WRITE(ipf_auth) */
680/* */
681/* This function flushs the fr_authpkts array of any packet data with */
682/* references still there. */
683/* It is expected that the caller has already acquired the correct locks or */
684/* set the priority level correctly for this to block out other code paths */
685/* into these data structures. */
686/* ------------------------------------------------------------------------ */
687int fr_authflush()
688{
689 register int i, num_flushed;
690 mb_t *m;
691
692 if (fr_auth_lock)
693 return -1;
694
695 num_flushed = 0;
696
697 for (i = 0 ; i < fr_authsize; i++) {
698 m = fr_authpkts[i];
699 if (m != NULL) {
700 FREE_MB_T(m);
701 fr_authpkts[i] = NULL;
702 fr_auth[i].fra_index = -1;
703 /* perhaps add & use a flush counter inst.*/
704 fr_authstats.fas_expire++;
705 fr_authused--;
706 num_flushed++;
707 }
708 }
709
710 fr_authstart = 0;
711 fr_authend = 0;
712 fr_authnext = 0;
713
714 return num_flushed;
715}
716
717
718/* ------------------------------------------------------------------------ */
719/* Function: fr_auth_waiting */
720/* Returns: int - 0 = no pakcets wiating, 1 = packets waiting. */
721/* Parameters: None */
722/* */
723/* Simple truth check to see if there are any packets waiting in the auth */
724/* queue. */
725/* ------------------------------------------------------------------------ */
726int fr_auth_waiting()
727{
728 return (fr_authused != 0);
729}
730
731
732/* ------------------------------------------------------------------------ */
733/* Function: fr_authgeniter */
734/* Returns: int - 0 == success, else error */
735/* Parameters: token(I) - pointer to ipftoken structure */
736/* itp(I) - pointer to ipfgeniter structure */
737/* */
738/* ------------------------------------------------------------------------ */
739int fr_authgeniter(token, itp)
740ipftoken_t *token;
741ipfgeniter_t *itp;
742{
743 frauthent_t *fae, *next, zero;
744 int error;
745
746 if (itp->igi_data == NULL)
747 return EFAULT;
748
749 if (itp->igi_type != IPFGENITER_AUTH)
750 return EINVAL;
751
752 fae = token->ipt_data;
753 READ_ENTER(&ipf_auth);
754 if (fae == NULL) {
755 next = fae_list;
756 } else {
757 next = fae->fae_next;
758 }
759
760 if (next != NULL) {
761 /*
762 * If we find an auth entry to use, bump its reference count
763 * so that it can be used for is_next when we come back.
764 */
765 ATOMIC_INC(next->fae_ref);
766 if (next->fae_next == NULL) {
767 ipf_freetoken(token);
768 token = NULL;
769 } else {
770 token->ipt_data = next;
771 }
772 } else {
773 bzero(&zero, sizeof(zero));
774 next = &zero;
775 }
776 RWLOCK_EXIT(&ipf_auth);
777
778 /*
779 * If we had a prior pointer to an auth entry, release it.
780 */
781 if (fae != NULL) {
782 WRITE_ENTER(&ipf_auth);
783 fr_authderef(&fae);
784 RWLOCK_EXIT(&ipf_auth);
785 }
786
787 /*
788 * This should arguably be via fr_outobj() so that the auth
789 * structure can (if required) be massaged going out.
790 */
791 error = COPYOUT(next, itp->igi_data, sizeof(*next));
792 if (error != 0)
793 error = EFAULT;
794
795 return error;
796}
797
798
799/* ------------------------------------------------------------------------ */
800/* Function: fr_authderef */
801/* Returns: None */
802/* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */
803/* Locks: WRITE(ipf_auth) */
804/* */
805/* This function unconditionally sets the pointer in the caller to NULL, */
806/* to make it clear that it should no longer use that pointer, and drops */
807/* the reference count on the structure by 1. If it reaches 0, free it up. */
808/* ------------------------------------------------------------------------ */
809void fr_authderef(faep)
810frauthent_t **faep;
811{
812 frauthent_t *fae;
813
814 fae = *faep;
815 *faep = NULL;
816
817 fae->fae_ref--;
818 if (fae->fae_ref == 0) {
819 KFREE(fae);
820 }
821}
822
823
824/* ------------------------------------------------------------------------ */
825/* Function: fr_authwait */
826/* Returns: int - 0 == success, else error */
827/* Parameters: data(I) - pointer to data from ioctl call */
828/* */
829/* This function is called when an application is waiting for a packet to */
830/* match an "auth" rule by issuing an SIOCAUTHW ioctl. If there is already */
831/* a packet waiting on the queue then we will return that _one_ immediately.*/
832/* If there are no packets present in the queue (fr_authpkts) then we go to */
833/* sleep. */
834/* ------------------------------------------------------------------------ */
835int fr_authwait(data)
836char *data;
837{
838 frauth_t auth, *au = &auth;
839 int error, len, i;
840 mb_t *m;
841 char *t;
842#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
843 (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
844 SPL_INT(s);
845#endif
846
847fr_authioctlloop:
848 error = fr_inobj(data, au, IPFOBJ_FRAUTH);
849 if (error != 0)
850 return error;
851
852 /*
853 * XXX Locks are held below over calls to copyout...a better
854 * solution needs to be found so this isn't necessary. The situation
855 * we are trying to guard against here is an error in the copyout
856 * steps should not cause the packet to "disappear" from the queue.
857 */
858 READ_ENTER(&ipf_auth);
859
860 /*
861 * If fr_authnext is not equal to fr_authend it will be because there
862 * is a packet waiting to be delt with in the fr_authpkts array. We
863 * copy as much of that out to user space as requested.
864 */
865 if (fr_authused > 0) {
866 while (fr_authpkts[fr_authnext] == NULL) {
867 fr_authnext++;
868 if (fr_authnext == fr_authsize)
869 fr_authnext = 0;
870 }
871
872 error = fr_outobj(data, &fr_auth[fr_authnext], IPFOBJ_FRAUTH);
873 if (error != 0)
874 return error;
875
876 if (auth.fra_len != 0 && auth.fra_buf != NULL) {
877 /*
878 * Copy packet contents out to user space if
879 * requested. Bail on an error.
880 */
881 m = fr_authpkts[fr_authnext];
882 len = MSGDSIZE(m);
883 if (len > auth.fra_len)
884 len = auth.fra_len;
885 auth.fra_len = len;
886
887 for (t = auth.fra_buf; m && (len > 0); ) {
888 i = MIN(M_LEN(m), len);
889 error = copyoutptr(MTOD(m, char *), &t, i);
890 len -= i;
891 t += i;
892 if (error != 0)
893 return error;
894 m = m->m_next;
895 }
896 }
897 RWLOCK_EXIT(&ipf_auth);
898
899 SPL_NET(s);
900 WRITE_ENTER(&ipf_auth);
901 fr_authnext++;
902 if (fr_authnext == fr_authsize)
903 fr_authnext = 0;
904 RWLOCK_EXIT(&ipf_auth);
905 SPL_X(s);
906
907 return 0;
908 }
909 RWLOCK_EXIT(&ipf_auth);
910
911 MUTEX_ENTER(&ipf_authmx);
912#ifdef _KERNEL
913# if SOLARIS
914 error = 0;
915 if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
916 error = EINTR;
917# else /* SOLARIS */
918# ifdef __hpux
919 {
920 lock_t *l;
921
922 l = get_sleep_lock(&fr_authnext);
923 error = sleep(&fr_authnext, PZERO+1);
924 spinunlock(l);
925 }
926# else
927# ifdef __osf__
928 error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
929 &ipf_authmx, MS_LOCK_SIMPLE);
930# else
931 error = SLEEP(&fr_authnext, "fr_authnext");
932# endif /* __osf__ */
933# endif /* __hpux */
934# endif /* SOLARIS */
935#endif
936 MUTEX_EXIT(&ipf_authmx);
937 if (error == 0)
938 goto fr_authioctlloop;
939 return error;
940}
941
942
943/* ------------------------------------------------------------------------ */
944/* Function: fr_authreply */
945/* Returns: int - 0 == success, else error */
946/* Parameters: data(I) - pointer to data from ioctl call */
947/* */
948/* This function is called by an application when it wants to return a */
949/* decision on a packet using the SIOCAUTHR ioctl. This is after it has */
950/* received information using an SIOCAUTHW. The decision returned in the */
951/* form of flags, the same as those used in each rule. */
952/* ------------------------------------------------------------------------ */
953int fr_authreply(data)
954char *data;
955{
956 frauth_t auth, *au = &auth, *fra;
957 int error, i;
958 mb_t *m;
959 SPL_INT(s);
960
961 error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
962 if (error != 0)
963 return error;
964
965 SPL_NET(s);
966 WRITE_ENTER(&ipf_auth);
967
968 i = au->fra_index;
969 fra = fr_auth + i;
970 error = 0;
971
972 /*
973 * Check the validity of the information being returned with two simple
974 * checks. First, the auth index value should be within the size of
975 * the array and second the packet id being returned should also match.
976 */
977 if ((i < 0) || (i >= fr_authsize) ||
978 (fra->fra_info.fin_id != au->fra_info.fin_id)) {
979 RWLOCK_EXIT(&ipf_auth);
980 SPL_X(s);
981 return ESRCH;
982 }
983
984 m = fr_authpkts[i];
985 fra->fra_index = -2;
986 fra->fra_pass = au->fra_pass;
987 fr_authpkts[i] = NULL;
988
989 RWLOCK_EXIT(&ipf_auth);
990
991 /*
992 * Re-insert the packet back into the packet stream flowing through
993 * the kernel in a manner that will mean IPFilter sees the packet
994 * again. This is not the same as is done with fastroute,
995 * deliberately, as we want to resume the normal packet processing
996 * path for it.
997 */
998#ifdef _KERNEL
999 if ((m != NULL) && (au->fra_info.fin_out != 0)) {
1000 error = ipf_inject(&fra->fra_info, m);
1001 if (error != 0) {
1002 error = ENOBUFS;
1003 fr_authstats.fas_sendfail++;
1004 } else {
1005 fr_authstats.fas_sendok++;
1006 }
1007 } else if (m) {
1008 error = ipf_inject(&fra->fra_info, m);
1009 if (error != 0) {
1010 error = ENOBUFS;
1011 fr_authstats.fas_quefail++;
1012 } else {
1013 fr_authstats.fas_queok++;
1014 }
1015 } else {
1016 error = EINVAL;
1017 }
1018
1019 /*
1020 * If we experience an error which will result in the packet
1021 * not being processed, make sure we advance to the next one.
1022 */
1023 if (error == ENOBUFS) {
1024 WRITE_ENTER(&ipf_auth);
1025 fr_authused--;
1026 fra->fra_index = -1;
1027 fra->fra_pass = 0;
1028 if (i == fr_authstart) {
1029 while (fra->fra_index == -1) {
1030 i++;
1031 if (i == fr_authsize)
1032 i = 0;
1033 fr_authstart = i;
1034 if (i == fr_authend)
1035 break;
1036 }
1037 if (fr_authstart == fr_authend) {
1038 fr_authnext = 0;
1039 fr_authstart = fr_authend = 0;
1040 }
1041 }
1042 RWLOCK_EXIT(&ipf_auth);
1043 }
1044#endif /* _KERNEL */
1045 SPL_X(s);
1046
1047 return 0;
1048}
2
3/*
4 * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij.
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#include <sys/errno.h>
15#include <sys/types.h>
16#include <sys/param.h>
17#include <sys/time.h>
18#include <sys/file.h>
19#if !defined(_KERNEL)
20# include <stdio.h>
21# include <stdlib.h>
22# include <string.h>
23# define _KERNEL
24# ifdef __OpenBSD__
25struct file;
26# endif
27# include <sys/uio.h>
28# undef _KERNEL
29#endif
30#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
31# include <sys/filio.h>
32# include <sys/fcntl.h>
33#else
34# include <sys/ioctl.h>
35#endif
36#if !defined(linux)
37# include <sys/protosw.h>
38#endif
39#include <sys/socket.h>
40#if defined(_KERNEL)
41# include <sys/systm.h>
42# if !defined(__SVR4) && !defined(__svr4__) && !defined(linux)
43# include <sys/mbuf.h>
44# endif
45#endif
46#if defined(__SVR4) || defined(__svr4__)
47# include <sys/filio.h>
48# include <sys/byteorder.h>
49# ifdef _KERNEL
50# include <sys/dditypes.h>
51# endif
52# include <sys/stream.h>
53# include <sys/kmem.h>
54#endif
55#if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \
56 (defined(__FreeBSD_version) &&(__FreeBSD_version >= 400000))
57# include <sys/queue.h>
58#endif
59#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
60# include <machine/cpu.h>
61#endif
62#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)
63# include <sys/proc.h>
64#endif
65#include <net/if.h>
66#ifdef sun
67# include <net/af.h>
68#endif
69#include <net/route.h>
70#include <netinet/in.h>
71#include <netinet/in_systm.h>
72#include <netinet/ip.h>
73#if !defined(_KERNEL) && defined(__FreeBSD_version) && \
74 __FreeBSD_version >= 800049
75# define V_ip_do_randomid ip_do_randomid
76# define V_ip_id ip_id
77#endif
78#if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi)
79# define KERNEL
80# define _KERNEL
81# define NOT_KERNEL
82#endif
83#if !defined(linux)
84# include <netinet/ip_var.h>
85#endif
86#ifdef NOT_KERNEL
87# undef _KERNEL
88# undef KERNEL
89#endif
90#include <netinet/tcp.h>
91#if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */
92extern struct ifqueue ipintrq; /* ip packet input queue */
93#else
94# if !defined(__hpux) && !defined(linux)
95# if __FreeBSD_version >= 300000
96# include <net/if_var.h>
97# if __FreeBSD_version >= 500042
98# define IF_QFULL _IF_QFULL
99# define IF_DROP _IF_DROP
100# endif /* __FreeBSD_version >= 500042 */
101# endif
102# include <netinet/in_var.h>
103# include <netinet/tcp_fsm.h>
104# endif
105#endif
106#include <netinet/udp.h>
107#include <netinet/ip_icmp.h>
108#include "netinet/ip_compat.h"
109#include <netinet/tcpip.h>
110#include "netinet/ip_fil.h"
111#include "netinet/ip_auth.h"
112#if !defined(MENTAT) && !defined(linux)
113# include <net/netisr.h>
114# ifdef __FreeBSD__
115# include <machine/cpufunc.h>
116# endif
117#endif
118#if (__FreeBSD_version >= 300000)
119# include <sys/malloc.h>
120# if defined(_KERNEL) && !defined(IPFILTER_LKM)
121# include <sys/libkern.h>
122# include <sys/systm.h>
123# endif
124#endif
125/* END OF INCLUDES */
126
127#if !defined(lint)
128static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 183550 2008-10-02 15:37:58Z zec $";
129/* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.24 2007/09/09 11:32:04 darrenr Exp $"; */
130#endif
131
132
133#if SOLARIS && defined(_KERNEL)
134extern kcondvar_t ipfauthwait;
135extern struct pollhead iplpollhead[IPL_LOGSIZE];
136#endif /* SOLARIS */
137#if defined(linux) && defined(_KERNEL)
138wait_queue_head_t fr_authnext_linux;
139#endif
140
141int fr_authsize = FR_NUMAUTH;
142int fr_authused = 0;
143int fr_defaultauthage = 600;
144int fr_auth_lock = 0;
145int fr_auth_init = 0;
146fr_authstat_t fr_authstats;
147static frauth_t *fr_auth = NULL;
148mb_t **fr_authpkts = NULL;
149int fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
150frauthent_t *fae_list = NULL;
151frentry_t *ipauth = NULL,
152 *fr_authlist = NULL;
153
154void fr_authderef __P((frauthent_t **));
155int fr_authgeniter __P((ipftoken_t *, ipfgeniter_t *));
156int fr_authreply __P((char *));
157int fr_authwait __P((char *));
158
159/* ------------------------------------------------------------------------ */
160/* Function: fr_authinit */
161/* Returns: int - 0 == success, else error */
162/* Parameters: None */
163/* */
164/* Allocate memory and initialise data structures used in handling auth */
165/* rules. */
166/* ------------------------------------------------------------------------ */
167int fr_authinit()
168{
169 KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth));
170 if (fr_auth != NULL)
171 bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth));
172 else
173 return -1;
174
175 KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts));
176 if (fr_authpkts != NULL)
177 bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
178 else
179 return -2;
180
181 MUTEX_INIT(&ipf_authmx, "ipf auth log mutex");
182 RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock");
183#if SOLARIS && defined(_KERNEL)
184 cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL);
185#endif
186#if defined(linux) && defined(_KERNEL)
187 init_waitqueue_head(&fr_authnext_linux);
188#endif
189
190 fr_auth_init = 1;
191
192 return 0;
193}
194
195
196/* ------------------------------------------------------------------------ */
197/* Function: fr_checkauth */
198/* Returns: frentry_t* - pointer to ipf rule if match found, else NULL */
199/* Parameters: fin(I) - pointer to ipftoken structure */
200/* passp(I) - pointer to ipfgeniter structure */
201/* */
202/* Check if a packet has authorization. If the packet is found to match an */
203/* authorization result and that would result in a feedback loop (i.e. it */
204/* will end up returning FR_AUTH) then return FR_BLOCK instead. */
205/* ------------------------------------------------------------------------ */
206frentry_t *fr_checkauth(fin, passp)
207fr_info_t *fin;
208u_32_t *passp;
209{
210 frentry_t *fr;
211 frauth_t *fra;
212 u_32_t pass;
213 u_short id;
214 ip_t *ip;
215 int i;
216
217 if (fr_auth_lock || !fr_authused)
218 return NULL;
219
220 ip = fin->fin_ip;
221 id = ip->ip_id;
222
223 READ_ENTER(&ipf_auth);
224 for (i = fr_authstart; i != fr_authend; ) {
225 /*
226 * index becomes -2 only after an SIOCAUTHW. Check this in
227 * case the same packet gets sent again and it hasn't yet been
228 * auth'd.
229 */
230 fra = fr_auth + i;
231 if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) &&
232 !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) {
233 /*
234 * Avoid feedback loop.
235 */
236 if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass)))
237 pass = FR_BLOCK;
238 /*
239 * Create a dummy rule for the stateful checking to
240 * use and return. Zero out any values we don't
241 * trust from userland!
242 */
243 if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) &&
244 (fin->fin_flx & FI_FRAG))) {
245 KMALLOC(fr, frentry_t *);
246 if (fr) {
247 bcopy((char *)fra->fra_info.fin_fr,
248 (char *)fr, sizeof(*fr));
249 fr->fr_grp = NULL;
250 fr->fr_ifa = fin->fin_ifp;
251 fr->fr_func = NULL;
252 fr->fr_ref = 1;
253 fr->fr_flags = pass;
254 fr->fr_ifas[1] = NULL;
255 fr->fr_ifas[2] = NULL;
256 fr->fr_ifas[3] = NULL;
257 }
258 } else
259 fr = fra->fra_info.fin_fr;
260 fin->fin_fr = fr;
261 RWLOCK_EXIT(&ipf_auth);
262
263 WRITE_ENTER(&ipf_auth);
264 /*
265 * fr_authlist is populated with the rules malloc'd
266 * above and only those.
267 */
268 if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) {
269 fr->fr_next = fr_authlist;
270 fr_authlist = fr;
271 }
272 fr_authstats.fas_hits++;
273 fra->fra_index = -1;
274 fr_authused--;
275 if (i == fr_authstart) {
276 while (fra->fra_index == -1) {
277 i++;
278 fra++;
279 if (i == fr_authsize) {
280 i = 0;
281 fra = fr_auth;
282 }
283 fr_authstart = i;
284 if (i == fr_authend)
285 break;
286 }
287 if (fr_authstart == fr_authend) {
288 fr_authnext = 0;
289 fr_authstart = fr_authend = 0;
290 }
291 }
292 RWLOCK_EXIT(&ipf_auth);
293 if (passp != NULL)
294 *passp = pass;
295 ATOMIC_INC64(fr_authstats.fas_hits);
296 return fr;
297 }
298 i++;
299 if (i == fr_authsize)
300 i = 0;
301 }
302 fr_authstats.fas_miss++;
303 RWLOCK_EXIT(&ipf_auth);
304 ATOMIC_INC64(fr_authstats.fas_miss);
305 return NULL;
306}
307
308
309/* ------------------------------------------------------------------------ */
310/* Function: fr_newauth */
311/* Returns: int - 1 == success, 0 = did not put packet on auth queue */
312/* Parameters: m(I) - pointer to mb_t with packet in it */
313/* fin(I) - pointer to packet information */
314/* */
315/* Check if we have room in the auth array to hold details for another */
316/* packet. If we do, store it and wake up any user programs which are */
317/* waiting to hear about these events. */
318/* ------------------------------------------------------------------------ */
319int fr_newauth(m, fin)
320mb_t *m;
321fr_info_t *fin;
322{
323#if defined(_KERNEL) && defined(MENTAT)
324 qpktinfo_t *qpi = fin->fin_qpi;
325#endif
326 frauth_t *fra;
327#if !defined(sparc) && !defined(m68k)
328 ip_t *ip;
329#endif
330 int i;
331
332 if (fr_auth_lock)
333 return 0;
334
335 WRITE_ENTER(&ipf_auth);
336 if (((fr_authend + 1) % fr_authsize) == fr_authstart) {
337 fr_authstats.fas_nospace++;
338 RWLOCK_EXIT(&ipf_auth);
339 return 0;
340 }
341
342 fr_authstats.fas_added++;
343 fr_authused++;
344 i = fr_authend++;
345 if (fr_authend == fr_authsize)
346 fr_authend = 0;
347 fra = fr_auth + i;
348 fra->fra_index = i;
349 RWLOCK_EXIT(&ipf_auth);
350
351 if (fin->fin_fr != NULL)
352 fra->fra_pass = fin->fin_fr->fr_flags;
353 else
354 fra->fra_pass = 0;
355 fra->fra_age = fr_defaultauthage;
356 bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin));
357#if !defined(sparc) && !defined(m68k)
358 /*
359 * No need to copyback here as we want to undo the changes, not keep
360 * them.
361 */
362 ip = fin->fin_ip;
363# if defined(MENTAT) && defined(_KERNEL)
364 if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4))
365# endif
366 {
367 register u_short bo;
368
369 bo = ip->ip_len;
370 ip->ip_len = htons(bo);
371 bo = ip->ip_off;
372 ip->ip_off = htons(bo);
373 }
374#endif
375#if SOLARIS && defined(_KERNEL)
376 COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname);
377 m->b_rptr -= qpi->qpi_off;
378 fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
379# if !defined(_INET_IP_STACK_H)
380 fra->fra_q = qpi->qpi_q; /* The queue can disappear! */
381# endif
382 fra->fra_m = *fin->fin_mp;
383 fra->fra_info.fin_mp = &fra->fra_m;
384 cv_signal(&ipfauthwait);
385 pollwakeup(&iplpollhead[IPL_LOGAUTH], POLLIN|POLLRDNORM);
386#else
387 fr_authpkts[i] = m;
388 WAKEUP(&fr_authnext,0);
389#endif
390 return 1;
391}
392
393
394/* ------------------------------------------------------------------------ */
395/* Function: fr_auth_ioctl */
396/* Returns: int - 0 == success, else error */
397/* Parameters: data(IO) - pointer to ioctl data */
398/* cmd(I) - ioctl command */
399/* mode(I) - mode flags associated with open descriptor */
400/* uid(I) - uid associatd with application making the call */
401/* ctx(I) - pointer for context */
402/* */
403/* This function handles all of the ioctls recognised by the auth component */
404/* in IPFilter - ie ioctls called on an open fd for /dev/ipauth */
405/* ------------------------------------------------------------------------ */
406int fr_auth_ioctl(data, cmd, mode, uid, ctx)
407caddr_t data;
408ioctlcmd_t cmd;
409int mode, uid;
410void *ctx;
411{
412 int error = 0, i;
413 SPL_INT(s);
414
415 switch (cmd)
416 {
417 case SIOCGENITER :
418 {
419 ipftoken_t *token;
420 ipfgeniter_t iter;
421
422 error = fr_inobj(data, &iter, IPFOBJ_GENITER);
423 if (error != 0)
424 break;
425
426 SPL_SCHED(s);
427 token = ipf_findtoken(IPFGENITER_AUTH, uid, ctx);
428 if (token != NULL)
429 error = fr_authgeniter(token, &iter);
430 else
431 error = ESRCH;
432 RWLOCK_EXIT(&ipf_tokens);
433 SPL_X(s);
434
435 break;
436 }
437
438 case SIOCADAFR :
439 case SIOCRMAFR :
440 if (!(mode & FWRITE))
441 error = EPERM;
442 else
443 error = frrequest(IPL_LOGAUTH, cmd, data,
444 fr_active, 1);
445 break;
446
447 case SIOCSTLCK :
448 if (!(mode & FWRITE)) {
449 error = EPERM;
450 break;
451 }
452 error = fr_lock(data, &fr_auth_lock);
453 break;
454
455 case SIOCATHST:
456 fr_authstats.fas_faelist = fae_list;
457 error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT);
458 break;
459
460 case SIOCIPFFL:
461 SPL_NET(s);
462 WRITE_ENTER(&ipf_auth);
463 i = fr_authflush();
464 RWLOCK_EXIT(&ipf_auth);
465 SPL_X(s);
466 error = BCOPYOUT((char *)&i, data, sizeof(i));
467 if (error != 0)
468 error = EFAULT;
469 break;
470
471 case SIOCAUTHW:
472 error = fr_authwait(data);
473 break;
474
475 case SIOCAUTHR:
476 error = fr_authreply(data);
477 break;
478
479 default :
480 error = EINVAL;
481 break;
482 }
483 return error;
484}
485
486
487/* ------------------------------------------------------------------------ */
488/* Function: fr_authunload */
489/* Returns: None */
490/* Parameters: None */
491/* */
492/* Free all network buffer memory used to keep saved packets. */
493/* ------------------------------------------------------------------------ */
494void fr_authunload()
495{
496 register int i;
497 register frauthent_t *fae, **faep;
498 frentry_t *fr, **frp;
499 mb_t *m;
500
501 if (fr_auth != NULL) {
502 KFREES(fr_auth, fr_authsize * sizeof(*fr_auth));
503 fr_auth = NULL;
504 }
505
506 if (fr_authpkts != NULL) {
507 for (i = 0; i < fr_authsize; i++) {
508 m = fr_authpkts[i];
509 if (m != NULL) {
510 FREE_MB_T(m);
511 fr_authpkts[i] = NULL;
512 }
513 }
514 KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts));
515 fr_authpkts = NULL;
516 }
517
518 faep = &fae_list;
519 while ((fae = *faep) != NULL) {
520 *faep = fae->fae_next;
521 KFREE(fae);
522 }
523 ipauth = NULL;
524
525 if (fr_authlist != NULL) {
526 for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
527 if (fr->fr_ref == 1) {
528 *frp = fr->fr_next;
529 KFREE(fr);
530 } else
531 frp = &fr->fr_next;
532 }
533 }
534
535 if (fr_auth_init == 1) {
536# if SOLARIS && defined(_KERNEL)
537 cv_destroy(&ipfauthwait);
538# endif
539 MUTEX_DESTROY(&ipf_authmx);
540 RW_DESTROY(&ipf_auth);
541
542 fr_auth_init = 0;
543 }
544}
545
546
547/* ------------------------------------------------------------------------ */
548/* Function: fr_authexpire */
549/* Returns: None */
550/* Parameters: None */
551/* */
552/* Slowly expire held auth records. Timeouts are set in expectation of */
553/* this being called twice per second. */
554/* ------------------------------------------------------------------------ */
555void fr_authexpire()
556{
557 frauthent_t *fae, **faep;
558 frentry_t *fr, **frp;
559 frauth_t *fra;
560 mb_t *m;
561 int i;
562 SPL_INT(s);
563
564 if (fr_auth_lock)
565 return;
566
567 SPL_NET(s);
568 WRITE_ENTER(&ipf_auth);
569 for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) {
570 fra->fra_age--;
571 if ((fra->fra_age == 0) && (m = fr_authpkts[i])) {
572 FREE_MB_T(m);
573 fr_authpkts[i] = NULL;
574 fr_auth[i].fra_index = -1;
575 fr_authstats.fas_expire++;
576 fr_authused--;
577 }
578 }
579
580 /*
581 * Expire pre-auth rules
582 */
583 for (faep = &fae_list; ((fae = *faep) != NULL); ) {
584 fae->fae_age--;
585 if (fae->fae_age == 0) {
586 fr_authderef(&fae);
587 fr_authstats.fas_expire++;
588 } else
589 faep = &fae->fae_next;
590 }
591 if (fae_list != NULL)
592 ipauth = &fae_list->fae_fr;
593 else
594 ipauth = NULL;
595
596 for (frp = &fr_authlist; ((fr = *frp) != NULL); ) {
597 if (fr->fr_ref == 1) {
598 *frp = fr->fr_next;
599 KFREE(fr);
600 } else
601 frp = &fr->fr_next;
602 }
603 RWLOCK_EXIT(&ipf_auth);
604 SPL_X(s);
605}
606
607
608/* ------------------------------------------------------------------------ */
609/* Function: fr_preauthcmd */
610/* Returns: int - 0 == success, else error */
611/* Parameters: cmd(I) - ioctl command for rule */
612/* fr(I) - pointer to ipf rule */
613/* fptr(I) - pointer to caller's 'fr' */
614/* */
615/* ------------------------------------------------------------------------ */
616int fr_preauthcmd(cmd, fr, frptr)
617ioctlcmd_t cmd;
618frentry_t *fr, **frptr;
619{
620 frauthent_t *fae, **faep;
621 int error = 0;
622 SPL_INT(s);
623
624 if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR))
625 return EIO;
626
627 for (faep = &fae_list; ((fae = *faep) != NULL); ) {
628 if (&fae->fae_fr == fr)
629 break;
630 else
631 faep = &fae->fae_next;
632 }
633
634 if (cmd == (ioctlcmd_t)SIOCRMAFR) {
635 if (fr == NULL || frptr == NULL)
636 error = EINVAL;
637 else if (fae == NULL)
638 error = ESRCH;
639 else {
640 SPL_NET(s);
641 WRITE_ENTER(&ipf_auth);
642 *faep = fae->fae_next;
643 if (ipauth == &fae->fae_fr)
644 ipauth = fae_list ? &fae_list->fae_fr : NULL;
645 RWLOCK_EXIT(&ipf_auth);
646 SPL_X(s);
647
648 KFREE(fae);
649 }
650 } else if (fr != NULL && frptr != NULL) {
651 KMALLOC(fae, frauthent_t *);
652 if (fae != NULL) {
653 bcopy((char *)fr, (char *)&fae->fae_fr,
654 sizeof(*fr));
655 SPL_NET(s);
656 WRITE_ENTER(&ipf_auth);
657 fae->fae_age = fr_defaultauthage;
658 fae->fae_fr.fr_hits = 0;
659 fae->fae_fr.fr_next = *frptr;
660 fae->fae_ref = 1;
661 *frptr = &fae->fae_fr;
662 fae->fae_next = *faep;
663 *faep = fae;
664 ipauth = &fae_list->fae_fr;
665 RWLOCK_EXIT(&ipf_auth);
666 SPL_X(s);
667 } else
668 error = ENOMEM;
669 } else
670 error = EINVAL;
671 return error;
672}
673
674
675/* ------------------------------------------------------------------------ */
676/* Function: fr_authflush */
677/* Returns: int - number of auth entries flushed */
678/* Parameters: None */
679/* Locks: WRITE(ipf_auth) */
680/* */
681/* This function flushs the fr_authpkts array of any packet data with */
682/* references still there. */
683/* It is expected that the caller has already acquired the correct locks or */
684/* set the priority level correctly for this to block out other code paths */
685/* into these data structures. */
686/* ------------------------------------------------------------------------ */
687int fr_authflush()
688{
689 register int i, num_flushed;
690 mb_t *m;
691
692 if (fr_auth_lock)
693 return -1;
694
695 num_flushed = 0;
696
697 for (i = 0 ; i < fr_authsize; i++) {
698 m = fr_authpkts[i];
699 if (m != NULL) {
700 FREE_MB_T(m);
701 fr_authpkts[i] = NULL;
702 fr_auth[i].fra_index = -1;
703 /* perhaps add & use a flush counter inst.*/
704 fr_authstats.fas_expire++;
705 fr_authused--;
706 num_flushed++;
707 }
708 }
709
710 fr_authstart = 0;
711 fr_authend = 0;
712 fr_authnext = 0;
713
714 return num_flushed;
715}
716
717
718/* ------------------------------------------------------------------------ */
719/* Function: fr_auth_waiting */
720/* Returns: int - 0 = no pakcets wiating, 1 = packets waiting. */
721/* Parameters: None */
722/* */
723/* Simple truth check to see if there are any packets waiting in the auth */
724/* queue. */
725/* ------------------------------------------------------------------------ */
726int fr_auth_waiting()
727{
728 return (fr_authused != 0);
729}
730
731
732/* ------------------------------------------------------------------------ */
733/* Function: fr_authgeniter */
734/* Returns: int - 0 == success, else error */
735/* Parameters: token(I) - pointer to ipftoken structure */
736/* itp(I) - pointer to ipfgeniter structure */
737/* */
738/* ------------------------------------------------------------------------ */
739int fr_authgeniter(token, itp)
740ipftoken_t *token;
741ipfgeniter_t *itp;
742{
743 frauthent_t *fae, *next, zero;
744 int error;
745
746 if (itp->igi_data == NULL)
747 return EFAULT;
748
749 if (itp->igi_type != IPFGENITER_AUTH)
750 return EINVAL;
751
752 fae = token->ipt_data;
753 READ_ENTER(&ipf_auth);
754 if (fae == NULL) {
755 next = fae_list;
756 } else {
757 next = fae->fae_next;
758 }
759
760 if (next != NULL) {
761 /*
762 * If we find an auth entry to use, bump its reference count
763 * so that it can be used for is_next when we come back.
764 */
765 ATOMIC_INC(next->fae_ref);
766 if (next->fae_next == NULL) {
767 ipf_freetoken(token);
768 token = NULL;
769 } else {
770 token->ipt_data = next;
771 }
772 } else {
773 bzero(&zero, sizeof(zero));
774 next = &zero;
775 }
776 RWLOCK_EXIT(&ipf_auth);
777
778 /*
779 * If we had a prior pointer to an auth entry, release it.
780 */
781 if (fae != NULL) {
782 WRITE_ENTER(&ipf_auth);
783 fr_authderef(&fae);
784 RWLOCK_EXIT(&ipf_auth);
785 }
786
787 /*
788 * This should arguably be via fr_outobj() so that the auth
789 * structure can (if required) be massaged going out.
790 */
791 error = COPYOUT(next, itp->igi_data, sizeof(*next));
792 if (error != 0)
793 error = EFAULT;
794
795 return error;
796}
797
798
799/* ------------------------------------------------------------------------ */
800/* Function: fr_authderef */
801/* Returns: None */
802/* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */
803/* Locks: WRITE(ipf_auth) */
804/* */
805/* This function unconditionally sets the pointer in the caller to NULL, */
806/* to make it clear that it should no longer use that pointer, and drops */
807/* the reference count on the structure by 1. If it reaches 0, free it up. */
808/* ------------------------------------------------------------------------ */
809void fr_authderef(faep)
810frauthent_t **faep;
811{
812 frauthent_t *fae;
813
814 fae = *faep;
815 *faep = NULL;
816
817 fae->fae_ref--;
818 if (fae->fae_ref == 0) {
819 KFREE(fae);
820 }
821}
822
823
824/* ------------------------------------------------------------------------ */
825/* Function: fr_authwait */
826/* Returns: int - 0 == success, else error */
827/* Parameters: data(I) - pointer to data from ioctl call */
828/* */
829/* This function is called when an application is waiting for a packet to */
830/* match an "auth" rule by issuing an SIOCAUTHW ioctl. If there is already */
831/* a packet waiting on the queue then we will return that _one_ immediately.*/
832/* If there are no packets present in the queue (fr_authpkts) then we go to */
833/* sleep. */
834/* ------------------------------------------------------------------------ */
835int fr_authwait(data)
836char *data;
837{
838 frauth_t auth, *au = &auth;
839 int error, len, i;
840 mb_t *m;
841 char *t;
842#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \
843 (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000))
844 SPL_INT(s);
845#endif
846
847fr_authioctlloop:
848 error = fr_inobj(data, au, IPFOBJ_FRAUTH);
849 if (error != 0)
850 return error;
851
852 /*
853 * XXX Locks are held below over calls to copyout...a better
854 * solution needs to be found so this isn't necessary. The situation
855 * we are trying to guard against here is an error in the copyout
856 * steps should not cause the packet to "disappear" from the queue.
857 */
858 READ_ENTER(&ipf_auth);
859
860 /*
861 * If fr_authnext is not equal to fr_authend it will be because there
862 * is a packet waiting to be delt with in the fr_authpkts array. We
863 * copy as much of that out to user space as requested.
864 */
865 if (fr_authused > 0) {
866 while (fr_authpkts[fr_authnext] == NULL) {
867 fr_authnext++;
868 if (fr_authnext == fr_authsize)
869 fr_authnext = 0;
870 }
871
872 error = fr_outobj(data, &fr_auth[fr_authnext], IPFOBJ_FRAUTH);
873 if (error != 0)
874 return error;
875
876 if (auth.fra_len != 0 && auth.fra_buf != NULL) {
877 /*
878 * Copy packet contents out to user space if
879 * requested. Bail on an error.
880 */
881 m = fr_authpkts[fr_authnext];
882 len = MSGDSIZE(m);
883 if (len > auth.fra_len)
884 len = auth.fra_len;
885 auth.fra_len = len;
886
887 for (t = auth.fra_buf; m && (len > 0); ) {
888 i = MIN(M_LEN(m), len);
889 error = copyoutptr(MTOD(m, char *), &t, i);
890 len -= i;
891 t += i;
892 if (error != 0)
893 return error;
894 m = m->m_next;
895 }
896 }
897 RWLOCK_EXIT(&ipf_auth);
898
899 SPL_NET(s);
900 WRITE_ENTER(&ipf_auth);
901 fr_authnext++;
902 if (fr_authnext == fr_authsize)
903 fr_authnext = 0;
904 RWLOCK_EXIT(&ipf_auth);
905 SPL_X(s);
906
907 return 0;
908 }
909 RWLOCK_EXIT(&ipf_auth);
910
911 MUTEX_ENTER(&ipf_authmx);
912#ifdef _KERNEL
913# if SOLARIS
914 error = 0;
915 if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk))
916 error = EINTR;
917# else /* SOLARIS */
918# ifdef __hpux
919 {
920 lock_t *l;
921
922 l = get_sleep_lock(&fr_authnext);
923 error = sleep(&fr_authnext, PZERO+1);
924 spinunlock(l);
925 }
926# else
927# ifdef __osf__
928 error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0,
929 &ipf_authmx, MS_LOCK_SIMPLE);
930# else
931 error = SLEEP(&fr_authnext, "fr_authnext");
932# endif /* __osf__ */
933# endif /* __hpux */
934# endif /* SOLARIS */
935#endif
936 MUTEX_EXIT(&ipf_authmx);
937 if (error == 0)
938 goto fr_authioctlloop;
939 return error;
940}
941
942
943/* ------------------------------------------------------------------------ */
944/* Function: fr_authreply */
945/* Returns: int - 0 == success, else error */
946/* Parameters: data(I) - pointer to data from ioctl call */
947/* */
948/* This function is called by an application when it wants to return a */
949/* decision on a packet using the SIOCAUTHR ioctl. This is after it has */
950/* received information using an SIOCAUTHW. The decision returned in the */
951/* form of flags, the same as those used in each rule. */
952/* ------------------------------------------------------------------------ */
953int fr_authreply(data)
954char *data;
955{
956 frauth_t auth, *au = &auth, *fra;
957 int error, i;
958 mb_t *m;
959 SPL_INT(s);
960
961 error = fr_inobj(data, &auth, IPFOBJ_FRAUTH);
962 if (error != 0)
963 return error;
964
965 SPL_NET(s);
966 WRITE_ENTER(&ipf_auth);
967
968 i = au->fra_index;
969 fra = fr_auth + i;
970 error = 0;
971
972 /*
973 * Check the validity of the information being returned with two simple
974 * checks. First, the auth index value should be within the size of
975 * the array and second the packet id being returned should also match.
976 */
977 if ((i < 0) || (i >= fr_authsize) ||
978 (fra->fra_info.fin_id != au->fra_info.fin_id)) {
979 RWLOCK_EXIT(&ipf_auth);
980 SPL_X(s);
981 return ESRCH;
982 }
983
984 m = fr_authpkts[i];
985 fra->fra_index = -2;
986 fra->fra_pass = au->fra_pass;
987 fr_authpkts[i] = NULL;
988
989 RWLOCK_EXIT(&ipf_auth);
990
991 /*
992 * Re-insert the packet back into the packet stream flowing through
993 * the kernel in a manner that will mean IPFilter sees the packet
994 * again. This is not the same as is done with fastroute,
995 * deliberately, as we want to resume the normal packet processing
996 * path for it.
997 */
998#ifdef _KERNEL
999 if ((m != NULL) && (au->fra_info.fin_out != 0)) {
1000 error = ipf_inject(&fra->fra_info, m);
1001 if (error != 0) {
1002 error = ENOBUFS;
1003 fr_authstats.fas_sendfail++;
1004 } else {
1005 fr_authstats.fas_sendok++;
1006 }
1007 } else if (m) {
1008 error = ipf_inject(&fra->fra_info, m);
1009 if (error != 0) {
1010 error = ENOBUFS;
1011 fr_authstats.fas_quefail++;
1012 } else {
1013 fr_authstats.fas_queok++;
1014 }
1015 } else {
1016 error = EINVAL;
1017 }
1018
1019 /*
1020 * If we experience an error which will result in the packet
1021 * not being processed, make sure we advance to the next one.
1022 */
1023 if (error == ENOBUFS) {
1024 WRITE_ENTER(&ipf_auth);
1025 fr_authused--;
1026 fra->fra_index = -1;
1027 fra->fra_pass = 0;
1028 if (i == fr_authstart) {
1029 while (fra->fra_index == -1) {
1030 i++;
1031 if (i == fr_authsize)
1032 i = 0;
1033 fr_authstart = i;
1034 if (i == fr_authend)
1035 break;
1036 }
1037 if (fr_authstart == fr_authend) {
1038 fr_authnext = 0;
1039 fr_authstart = fr_authend = 0;
1040 }
1041 }
1042 RWLOCK_EXIT(&ipf_auth);
1043 }
1044#endif /* _KERNEL */
1045 SPL_X(s);
1046
1047 return 0;
1048}