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