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