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