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