]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/ipfilter/netinet/ip_scan.c
MFV r345495:
[FreeBSD/FreeBSD.git] / sys / contrib / ipfilter / netinet / ip_scan.c
1 /*
2  * Copyright (C) 2012 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  */
6 #if defined(KERNEL) || defined(_KERNEL)
7 # undef KERNEL
8 # undef _KERNEL
9 # define        KERNEL  1
10 # define        _KERNEL 1
11 #endif
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <sys/errno.h>
16 #if !defined(_KERNEL)
17 # include <stdlib.h>
18 # include <string.h>
19 # define _KERNEL
20 # include <sys/uio.h>
21 # undef _KERNEL
22 #else
23 # include <sys/systm.h>
24 # if !defined(__SVR4)
25 #  include <sys/mbuf.h>
26 # endif
27 #endif
28 #include <sys/socket.h>
29 # include <sys/ioccom.h>
30 #ifdef __FreeBSD__
31 # include <sys/filio.h>
32 # include <sys/malloc.h>
33 #else
34 # include <sys/ioctl.h>
35 #endif
36
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip.h>
40 #include <netinet/tcp.h>
41
42 #include <net/if.h>
43
44
45 #include "netinet/ip_compat.h"
46 #include "netinet/ip_fil.h"
47 #include "netinet/ip_state.h"
48 #include "netinet/ip_scan.h"
49 /* END OF INCLUDES */
50
51 #if !defined(lint)
52 static const char sccsid[] = "@(#)ip_state.c    1.8 6/5/96 (C) 1993-2000 Darren Reed";
53 static const char rcsid[] = "@(#)$Id$";
54 #endif
55
56 #ifdef  IPFILTER_SCAN   /* endif at bottom of file */
57
58
59 ipscan_t        *ipf_scan_list = NULL,
60                 *ipf_scan_tail = NULL;
61 ipscanstat_t    ipf_scan_stat;
62 # ifdef USE_MUTEXES
63 ipfrwlock_t     ipf_scan_rwlock;
64 # endif
65
66 # ifndef isalpha
67 #  define       isalpha(x)      (((x) >= 'A' && 'Z' >= (x)) || \
68                                  ((x) >= 'a' && 'z' >= (x)))
69 # endif
70
71
72 int ipf_scan_add __P((caddr_t));
73 int ipf_scan_remove __P((caddr_t));
74 struct ipscan *ipf_scan_lookup __P((char *));
75 int ipf_scan_matchstr __P((sinfo_t *, char *, int));
76 int ipf_scan_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
77 int ipf_scan_match __P((ipstate_t *));
78
79 static int      ipf_scan_inited = 0;
80
81
82 int
83 ipf_scan_init()
84 {
85         RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock");
86         ipf_scan_inited = 1;
87         return 0;
88 }
89
90
91 void
92 ipf_scan_unload(ipf_main_softc_t *arg)
93 {
94         if (ipf_scan_inited == 1) {
95                 RW_DESTROY(&ipf_scan_rwlock);
96                 ipf_scan_inited = 0;
97         }
98 }
99
100
101 int
102 ipf_scan_add(data)
103         caddr_t data;
104 {
105         ipscan_t *i, *isc;
106         int err;
107
108         KMALLOC(isc, ipscan_t *);
109         if (!isc) {
110                 ipf_interror = 90001;
111                 return ENOMEM;
112         }
113
114         err = copyinptr(data, isc, sizeof(*isc));
115         if (err) {
116                 KFREE(isc);
117                 return err;
118         }
119
120         WRITE_ENTER(&ipf_scan_rwlock);
121
122         i = ipf_scan_lookup(isc->ipsc_tag);
123         if (i != NULL) {
124                 RWLOCK_EXIT(&ipf_scan_rwlock);
125                 KFREE(isc);
126                 ipf_interror = 90002;
127                 return EEXIST;
128         }
129
130         if (ipf_scan_tail) {
131                 ipf_scan_tail->ipsc_next = isc;
132                 isc->ipsc_pnext = &ipf_scan_tail->ipsc_next;
133                 ipf_scan_tail = isc;
134         } else {
135                 ipf_scan_list = isc;
136                 ipf_scan_tail = isc;
137                 isc->ipsc_pnext = &ipf_scan_list;
138         }
139         isc->ipsc_next = NULL;
140
141         isc->ipsc_hits = 0;
142         isc->ipsc_fref = 0;
143         isc->ipsc_sref = 0;
144         isc->ipsc_active = 0;
145
146         ipf_scan_stat.iscs_entries++;
147         RWLOCK_EXIT(&ipf_scan_rwlock);
148         return 0;
149 }
150
151
152 int
153 ipf_scan_remove(data)
154         caddr_t data;
155 {
156         ipscan_t isc, *i;
157         int err;
158
159         err = copyinptr(data, &isc, sizeof(isc));
160         if (err)
161                 return err;
162
163         WRITE_ENTER(&ipf_scan_rwlock);
164
165         i = ipf_scan_lookup(isc.ipsc_tag);
166         if (i == NULL)
167                 err = ENOENT;
168         else {
169                 if (i->ipsc_fref) {
170                         RWLOCK_EXIT(&ipf_scan_rwlock);
171                         ipf_interror = 90003;
172                         return EBUSY;
173                 }
174
175                 *i->ipsc_pnext = i->ipsc_next;
176                 if (i->ipsc_next)
177                         i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
178                 else {
179                         if (i->ipsc_pnext == &ipf_scan_list)
180                                 ipf_scan_tail = NULL;
181                         else
182                                 ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext;
183                 }
184
185                 ipf_scan_stat.iscs_entries--;
186                 KFREE(i);
187         }
188         RWLOCK_EXIT(&ipf_scan_rwlock);
189         return err;
190 }
191
192
193 struct ipscan *
194 ipf_scan_lookup(tag)
195         char *tag;
196 {
197         ipscan_t *i;
198
199         for (i = ipf_scan_list; i; i = i->ipsc_next)
200                 if (!strcmp(i->ipsc_tag, tag))
201                         return i;
202         return NULL;
203 }
204
205
206 int
207 ipf_scan_attachfr(fr)
208         struct frentry *fr;
209 {
210         ipscan_t *i;
211
212         if (fr->fr_isctag != -1) {
213                 READ_ENTER(&ipf_scan_rwlock);
214                 i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names);
215                 if (i != NULL) {
216                         ATOMIC_INC32(i->ipsc_fref);
217                 }
218                 RWLOCK_EXIT(&ipf_scan_rwlock);
219                 if (i == NULL) {
220                         ipf_interror = 90004;
221                         return ENOENT;
222                 }
223                 fr->fr_isc = i;
224         }
225         return 0;
226 }
227
228
229 int
230 ipf_scan_attachis(is)
231         struct ipstate *is;
232 {
233         frentry_t *fr;
234         ipscan_t *i;
235
236         READ_ENTER(&ipf_scan_rwlock);
237         fr = is->is_rule;
238         if (fr != NULL) {
239                 i = fr->fr_isc;
240                 if ((i != NULL) && (i != (ipscan_t *)-1)) {
241                         is->is_isc = i;
242                         ATOMIC_INC32(i->ipsc_sref);
243                         if (i->ipsc_clen)
244                                 is->is_flags |= IS_SC_CLIENT;
245                         else
246                                 is->is_flags |= IS_SC_MATCHC;
247                         if (i->ipsc_slen)
248                                 is->is_flags |= IS_SC_SERVER;
249                         else
250                                 is->is_flags |= IS_SC_MATCHS;
251                 }
252         }
253         RWLOCK_EXIT(&ipf_scan_rwlock);
254         return 0;
255 }
256
257
258 int
259 ipf_scan_detachfr(fr)
260         struct frentry *fr;
261 {
262         ipscan_t *i;
263
264         i = fr->fr_isc;
265         if (i != NULL) {
266                 ATOMIC_DEC32(i->ipsc_fref);
267         }
268         return 0;
269 }
270
271
272 int
273 ipf_scan_detachis(is)
274         struct ipstate *is;
275 {
276         ipscan_t *i;
277
278         READ_ENTER(&ipf_scan_rwlock);
279         if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
280                 ATOMIC_DEC32(i->ipsc_sref);
281                 is->is_isc = NULL;
282                 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
283         }
284         RWLOCK_EXIT(&ipf_scan_rwlock);
285         return 0;
286 }
287
288
289 /*
290  * 'string' compare for scanning
291  */
292 int
293 ipf_scan_matchstr(sp, str, n)
294         sinfo_t *sp;
295         char *str;
296         int n;
297 {
298         char *s, *t, *up;
299         int i = n;
300
301         if (i > sp->s_len)
302                 i = sp->s_len;
303         up = str;
304
305         for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
306                 switch ((int)*t)
307                 {
308                 case '.' :
309                         if (*s != *up)
310                                 return 1;
311                         break;
312                 case '?' :
313                         if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
314                                 return 1;
315                         break;
316                 case '*' :
317                         break;
318                 }
319         return 0;
320 }
321
322
323 /*
324  * Returns 3 if both server and client match, 2 if just server,
325  * 1 if just client
326  */
327 int
328 ipf_scan_matchisc(isc, is, cl, sl, maxm)
329         ipscan_t *isc;
330         ipstate_t *is;
331         int cl, sl, maxm[2];
332 {
333         int i, j, k, n, ret = 0, flags;
334
335         flags = is->is_flags;
336
337         /*
338          * If we've already matched more than what is on offer, then
339          * assume we have a better match already and forget this one.
340          */
341         if (maxm != NULL) {
342                 if (isc->ipsc_clen < maxm[0])
343                         return 0;
344                 if (isc->ipsc_slen < maxm[1])
345                         return 0;
346                 j = maxm[0];
347                 k = maxm[1];
348         } else {
349                 j = 0;
350                 k = 0;
351         }
352
353         if (!isc->ipsc_clen)
354                 ret = 1;
355         else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
356                  cl && isc->ipsc_clen) {
357                 i = 0;
358                 n = MIN(cl, isc->ipsc_clen);
359                 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
360                         if (!ipf_scan_matchstr(&isc->ipsc_cl,
361                                                is->is_sbuf[0], n)) {
362                                 i++;
363                                 ret |= 1;
364                                 if (n > j)
365                                         j = n;
366                         }
367                 }
368         }
369
370         if (!isc->ipsc_slen)
371                 ret |= 2;
372         else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
373                  sl && isc->ipsc_slen) {
374                 i = 0;
375                 n = MIN(cl, isc->ipsc_slen);
376                 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
377                         if (!ipf_scan_matchstr(&isc->ipsc_sl,
378                                                is->is_sbuf[1], n)) {
379                                 i++;
380                                 ret |= 2;
381                                 if (n > k)
382                                         k = n;
383                         }
384                 }
385         }
386
387         if (maxm && (ret == 3)) {
388                 maxm[0] = j;
389                 maxm[1] = k;
390         }
391         return ret;
392 }
393
394
395 int
396 ipf_scan_match(is)
397         ipstate_t *is;
398 {
399         int i, j, k, n, cl, sl, maxm[2];
400         ipscan_t *isc, *lm;
401         tcpdata_t *t;
402
403         for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
404                 cl++;
405         for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
406                 sl++;
407
408         j = 0;
409         isc = is->is_isc;
410         if (isc != NULL) {
411                 /*
412                  * Known object to scan for.
413                  */
414                 i = ipf_scan_matchisc(isc, is, cl, sl, NULL);
415                 if (i & 1) {
416                         is->is_flags |= IS_SC_MATCHC;
417                         is->is_flags &= ~IS_SC_CLIENT;
418                 } else if (cl >= isc->ipsc_clen)
419                         is->is_flags &= ~IS_SC_CLIENT;
420                 if (i & 2) {
421                         is->is_flags |= IS_SC_MATCHS;
422                         is->is_flags &= ~IS_SC_SERVER;
423                 } else if (sl >= isc->ipsc_slen)
424                         is->is_flags &= ~IS_SC_SERVER;
425         } else {
426                 i = 0;
427                 lm = NULL;
428                 maxm[0] = 0;
429                 maxm[1] = 0;
430                 for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) {
431                         i = ipf_scan_matchisc(isc, is, cl, sl, maxm);
432                         if (i) {
433                                 /*
434                                  * We only want to remember the best match
435                                  * and the number of times we get a best
436                                  * match.
437                                  */
438                                 if ((j == 3) && (i < 3))
439                                         continue;
440                                 if ((i == 3) && (j != 3))
441                                         k = 1;
442                                 else
443                                         k++;
444                                 j = i;
445                                 lm = isc;
446                         }
447                 }
448                 if (k == 1)
449                         isc = lm;
450                 if (isc == NULL)
451                         return 0;
452
453                 /*
454                  * No matches or partial matches, so reset the respective
455                  * search flag.
456                  */
457                 if (!(j & 1))
458                         is->is_flags &= ~IS_SC_CLIENT;
459
460                 if (!(j & 2))
461                         is->is_flags &= ~IS_SC_SERVER;
462
463                 /*
464                  * If we found the best match, then set flags appropriately.
465                  */
466                 if ((j == 3) && (k == 1)) {
467                         is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
468                         is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
469                 }
470         }
471
472         /*
473          * If the acknowledged side of a connection has moved past the data in
474          * which we are interested, then reset respective flag.
475          */
476         t = &is->is_tcp.ts_data[0];
477         if (t->td_end > is->is_s0[0] + 15)
478                 is->is_flags &= ~IS_SC_CLIENT;
479
480         t = &is->is_tcp.ts_data[1];
481         if (t->td_end > is->is_s0[1] + 15)
482                 is->is_flags &= ~IS_SC_SERVER;
483
484         /*
485          * Matching complete ?
486          */
487         j = ISC_A_NONE;
488         if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
489                 j = isc->ipsc_action;
490                 ipf_scan_stat.iscs_acted++;
491         } else if ((is->is_isc != NULL) &&
492                    ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
493                    !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
494                 /*
495                  * Matching failed...
496                  */
497                 j = isc->ipsc_else;
498                 ipf_scan_stat.iscs_else++;
499         }
500
501         switch (j)
502         {
503         case  ISC_A_CLOSE :
504                 /*
505                  * If as a result of a successful match we are to
506                  * close a connection, change the "keep state" info.
507                  * to block packets and generate TCP RST's.
508                  */
509                 is->is_pass &= ~FR_RETICMP;
510                 is->is_pass |= FR_RETRST;
511                 break;
512         default :
513                 break;
514         }
515
516         return i;
517 }
518
519
520 /*
521  * check if a packet matches what we're scanning for
522  */
523 int
524 ipf_scan_packet(fin, is)
525         fr_info_t *fin;
526         ipstate_t *is;
527 {
528         int i, j, rv, dlen, off, thoff;
529         u_32_t seq, s0;
530         tcphdr_t *tcp;
531
532         rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
533         tcp = fin->fin_dp;
534         seq = ntohl(tcp->th_seq);
535
536         if (!is->is_s0[rv])
537                 return 1;
538
539         /*
540          * check if this packet has more data that falls within the first
541          * 16 bytes sent in either direction.
542          */
543         s0 = is->is_s0[rv];
544         off = seq - s0;
545         if ((off > 15) || (off < 0))
546                 return 1;
547         thoff = TCP_OFF(tcp) << 2;
548         dlen = fin->fin_dlen - thoff;
549         if (dlen <= 0)
550                 return 1;
551         if (dlen > 16)
552                 dlen = 16;
553         if (off + dlen > 16)
554                 dlen = 16 - off;
555
556         j = 0xffff >> (16 - dlen);
557         i = (0xffff & j) << off;
558 #ifdef _KERNEL
559         COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff,
560                  dlen, (caddr_t)is->is_sbuf[rv] + off);
561 #endif
562         is->is_smsk[rv] |= i;
563         for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
564                 j++;
565         if (j == 0)
566                 return 1;
567
568         (void) ipf_scan_match(is);
569 #if 0
570         /*
571          * There is the potential here for plain text passwords to get
572          * buffered and stored for some time...
573          */
574         if (!(is->is_flags & IS_SC_CLIENT))
575                 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
576         if (!(is->is_flags & IS_SC_SERVER))
577                 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
578 #endif
579         return 0;
580 }
581
582
583 int
584 ipf_scan_ioctl(data, cmd, mode, uid, ctx)
585         caddr_t data;
586         ioctlcmd_t cmd;
587         int mode, uid;
588         void *ctx;
589 {
590         ipscanstat_t ipscs;
591         int err = 0;
592
593         switch (cmd)
594         {
595         case SIOCADSCA :
596                 err = ipf_scan_add(data);
597                 break;
598         case SIOCRMSCA :
599                 err = ipf_scan_remove(data);
600                 break;
601         case SIOCGSCST :
602                 bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs));
603                 ipscs.iscs_list = ipf_scan_list;
604                 err = BCOPYOUT(&ipscs, data, sizeof(ipscs));
605                 if (err != 0) {
606                         ipf_interror = 90005;
607                         err = EFAULT;
608                 }
609                 break;
610         default :
611                 err = EINVAL;
612                 break;
613         }
614
615         return err;
616 }
617 #endif  /* IPFILTER_SCAN */