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