]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - sys/contrib/ipfilter/netinet/ip_auth.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.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(__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 */
92 extern 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)
128 static const char rcsid[] = "@(#)$FreeBSD$";
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)
134 extern kcondvar_t ipfauthwait;
135 extern struct pollhead iplpollhead[IPL_LOGSIZE];
136 #endif /* SOLARIS */
137 #if defined(linux) && defined(_KERNEL)
138 wait_queue_head_t     fr_authnext_linux;
139 #endif
140
141 int     fr_authsize = FR_NUMAUTH;
142 int     fr_authused = 0;
143 int     fr_defaultauthage = 600;
144 int     fr_auth_lock = 0;
145 int     fr_auth_init = 0;
146 fr_authstat_t   fr_authstats;
147 static frauth_t *fr_auth = NULL;
148 mb_t    **fr_authpkts = NULL;
149 int     fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
150 frauthent_t     *fae_list = NULL;
151 frentry_t       *ipauth = NULL,
152                 *fr_authlist = NULL;
153
154 void fr_authderef __P((frauthent_t **));
155 int fr_authgeniter __P((ipftoken_t *, ipfgeniter_t *));
156 int fr_authreply __P((char *));
157 int 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 /* ------------------------------------------------------------------------ */
167 int 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 /* ------------------------------------------------------------------------ */
206 frentry_t *fr_checkauth(fin, passp)
207 fr_info_t *fin;
208 u_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 /* ------------------------------------------------------------------------ */
319 int fr_newauth(m, fin)
320 mb_t *m;
321 fr_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 /* ------------------------------------------------------------------------ */
406 int fr_auth_ioctl(data, cmd, mode, uid, ctx)
407 caddr_t data;
408 ioctlcmd_t cmd;
409 int mode, uid;
410 void *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 /* ------------------------------------------------------------------------ */
494 void 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 /* ------------------------------------------------------------------------ */
555 void 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 /* ------------------------------------------------------------------------ */
616 int fr_preauthcmd(cmd, fr, frptr)
617 ioctlcmd_t cmd;
618 frentry_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 /* ------------------------------------------------------------------------ */
687 int 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 /* ------------------------------------------------------------------------ */
726 int 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 /* ------------------------------------------------------------------------ */
739 int fr_authgeniter(token, itp)
740 ipftoken_t *token;
741 ipfgeniter_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 /* ------------------------------------------------------------------------ */
809 void fr_authderef(faep)
810 frauthent_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 /* ------------------------------------------------------------------------ */
835 int fr_authwait(data)
836 char *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
847 fr_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 /* ------------------------------------------------------------------------ */
953 int fr_authreply(data)
954 char *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 }