massive conversion of types.
[freeradius.git] / src / lib / filters.c
1 /*
2  * ASCEND: @(#)filters.c        1.3 (95/07/25 00:55:30)
3  *
4  *      Copyright (c) 1994 Ascend Communications, Inc.
5  *      All rights reserved.
6  *
7  *      Permission to copy all or part of this material for any purpose is
8  *      granted provided that the above copyright notice and this paragraph
9  *      are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
10  *      AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
11  *      LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  *      FOR A PARTICULAR PURPOSE.
13  */
14
15 /* $Id$ */
16
17 /*
18  *  Alan DeKok's comments:  This code SUCKS.  Having a static string
19  *  instead of passing function parameters is a STUPID thing to do.
20  *
21  *  This code should be re-written to get rid of Ascend's copyright,
22  *  and to fix all of the horrible crap of the current implementation.
23  */
24
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <netinet/in.h>
31 #include <stdlib.h>
32 #include <sys/time.h>   /* gettimeofday() */
33
34 #include "libradius.h"
35
36 #define NO_TOKEN -1
37
38 /*
39  * Two types of filters are supported, GENERIC and IP.  The identifiers
40  * are:
41  */
42
43 #define RAD_FILTER_GENERIC      0
44 #define RAD_FILTER_IP           1
45 #define RAD_FILTER_IPX          2
46
47 /*
48  * Generic filters mask and match up to RAD_MAX_FILTER_LEN bytes
49  * starting at some offset.  The length is:
50  */
51 #define RAD_MAX_FILTER_LEN      6
52
53 /*
54  * ASCEND extensions for ABINARY filters
55  */
56
57 #define IPX_NODE_ADDR_LEN               6
58
59 typedef uint32_t                IpxNet;
60 typedef unsigned char           IpxNode[ IPX_NODE_ADDR_LEN ];
61 typedef unsigned short          IpxSocket;
62
63 #if ! defined( FALSE )
64 # define FALSE          0
65 # define TRUE           (! FALSE)
66 #endif
67
68 /*
69  * RadFilterComparison:
70  *
71  * An enumerated values for the IP filter port comparisons.
72  */
73 typedef enum {
74         RAD_NO_COMPARE,
75         RAD_COMPARE_LESS,
76         RAD_COMPARE_EQUAL,
77         RAD_COMPARE_GREATER,
78         RAD_COMPARE_NOT_EQUAL
79 } RadFilterComparison;
80
81     /*
82      * RadIpFilter:
83      *
84      * The binary format of an IP filter.  ALL fields are stored in
85      * network byte order.
86      *
87      *  srcip:          The source IP address.
88      *
89      *  dstip:          The destination IP address.
90      *
91      *  srcmask:        The number of leading one bits in the source address
92      *                  mask.  Specifies the bits of interest.
93      *
94      *  dstmask:        The number of leading one bits in the destination
95      *                  address mask. Specifies the bits of interest.
96      *
97      *  proto:          The IP protocol number
98      *
99      *  establised:     A boolean value.  TRUE when we care about the
100      *                  established state of a TCP connection.  FALSE when
101      *                  we dont care.
102      *
103      *  srcport:        TCP or UDP source port number.
104      *
105      *  dstport:        TCP or UDP destination port number.
106      *
107      *  srcPortCmp:     One of the values of the RadFilterComparison enumeration
108      *                  specifying how to compare the srcport value.
109      *
110      *  dstPortCmp:     One of the values of the RadFilterComparison enumeration
111      *                  specifying how to compare the dstport value.
112      *
113      *  fill:           Round things out to a dword boundary.
114      */
115 typedef struct radip {
116     uint32_t            srcip;
117     uint32_t            dstip;
118     unsigned char       srcmask;
119     unsigned char       dstmask;
120     unsigned char       proto;
121     unsigned char       established;
122     unsigned short      srcport;
123     unsigned short      dstport;
124     unsigned char       srcPortComp;
125     unsigned char       dstPortComp;
126     unsigned char       fill[4];        /* used to be fill[2] */
127 } RadIpFilter;
128
129     /*
130      * RadIpxFilter:
131      * The binary format of a GENERIC filter.  ALL fields are stored in
132      * network byte order.
133      *
134      *  srcIpxNet:      Source IPX Net address
135      *
136      *  srcIpxNode:     Source IPX Node address
137      *
138      *  srcIpxSoc:      Source IPX socket address
139      *
140      *  dstIpxNet:      Destination IPX Net address
141      *
142      *  dstIpxNode:     Destination IPX Node address
143      *
144      *  dstIpxSoc:      Destination IPX socket address
145      *
146      *  srcSocComp:     Source socket compare value
147      *
148      *  dstSocComp:     Destination socket compare value
149      *
150      */
151 typedef struct radipx {                         
152     IpxNet              srcIpxNet;                      /* LongWord */
153     IpxNode             srcIpxNode;                     /* Byte[6] */
154     IpxSocket           srcIpxSoc;                      /* Word */
155     IpxNet              dstIpxNet;                      /* LongWord */
156     IpxNode             dstIpxNode;                     /* Byte[6] */
157     IpxSocket           dstIpxSoc;                      /* Word */
158     unsigned char       srcSocComp;
159     unsigned char       dstSocComp;
160 } RadIpxFilter;
161
162     /*
163      * RadGenericFilter:
164      *
165      * The binary format of a GENERIC filter.  ALL fields are stored in
166      * network byte order.
167      *
168      *  offset:         Number of bytes into packet to start comparison.
169      *
170      *  len:            Number of bytes to mask and compare.  May not
171      *                  exceed RAD_MAX_FILTER_LEN.
172      *
173      *  more:           Boolean.  If non-zero the next filter entry is
174      *                  also to be applied to a packet.
175      *
176      *  mask:           A bit mask specifying the bits to compare.
177      *
178      *  value:          A value to compare against the masked bits at
179      *                  offset in a users packet.
180      *                  
181      *  compNeq:        Defines type of comarison (Equal or Notequal)
182      *                  default is Equal.
183      *
184      *  fill:           Round things out to a dword boundary
185      */
186 typedef struct radgeneric {
187     unsigned short      offset;
188     unsigned short      len;
189     unsigned short      more;
190     unsigned char       mask[ RAD_MAX_FILTER_LEN ];
191     unsigned char       value[ RAD_MAX_FILTER_LEN ];
192     unsigned char       compNeq;
193     unsigned char       fill[3];        /* used to be fill */
194 } RadGenericFilter;
195
196     /*
197      * RadFilter:
198      *
199      * A binary filter element.  Contains either a RadIpFilter or a
200      * RadGenericFilter.  All fields are stored in network byte order.
201      *
202      *  type:           Either RAD_FILTER_GENERIC or RAD_FILTER_IP.
203      *
204      *  forward:        TRUE if we should forward packets that match this
205      *                  filter, FALSE if we should drop packets that match
206      *                  this filter.
207      *
208      *  indirection:    TRUE if this is an input filter, FALSE if this is
209      *                  an output filter.
210      *
211      *  fill:           Round things out to a dword boundary.
212      *
213      *  u:              A union of
214      *                  ip:             An ip filter entry
215      *                  generic:        A generic filter entry
216      */
217 typedef struct filter {
218     unsigned char       type;
219     unsigned char       forward;
220     unsigned char       indirection;
221     unsigned char       fill;
222     union {
223         RadIpFilter      ip;
224         RadIpxFilter     ipx;
225         RadGenericFilter generic;
226     } u;
227 } RadFilter;
228 #define SIZEOF_RADFILTER 32
229
230 typedef struct {
231     const char* name;
232     unsigned int        value;
233 } KeywordStruct;
234
235     /*
236      * FilterPortType:
237      *
238      * Ascii names of some well known tcp/udp services.
239      * Used for filtering on a port type.
240      *
241      */
242 static KeywordStruct filterPortType[] = {
243     { "ftp-data", 20 },
244     { "ftp", 21 },
245     { "telnet", 23 },
246     { "smtp", 25 },
247     { "nameserver", 42 },
248     { "domain", 53 },
249     { "tftp", 69 },
250     { "gopher", 70 },
251     { "finger", 79 },
252     { "www", 80 },
253     { "kerberos", 88 },
254     { "hostname", 101 },
255     { "nntp", 119 },
256     { "ntp", 123 },
257     { "exec", 512 },
258     { "login", 513 },
259     { "cmd", 514 },
260     { "talk", 517 },
261     {  NULL , NO_TOKEN },
262 };
263
264 static KeywordStruct filterType[] = {
265     { "generic",RAD_FILTER_GENERIC},
266     { "ip",     RAD_FILTER_IP},
267     { "ipx",    RAD_FILTER_IPX},
268     { NULL,     NO_TOKEN},
269 };
270
271 typedef enum {
272     FILTER_IP_TYPE,
273     FILTER_GENERIC_TYPE,
274     FILTER_IN,
275     FILTER_OUT,
276     FILTER_FORWARD,
277     FILTER_DROP,
278     FILTER_GENERIC_OFFSET,
279     FILTER_GENERIC_MASK,
280     FILTER_GENERIC_VALUE,
281     FILTER_GENERIC_COMPNEQ,
282     FILTER_GENERIC_COMPEQ,
283     FILTER_MORE,
284     FILTER_IP_DST,
285     FILTER_IP_SRC,
286     FILTER_IP_PROTO,
287     FILTER_IP_DST_PORT,
288     FILTER_IP_SRC_PORT,
289     FILTER_EST,
290     FILTER_IPX_TYPE,
291     FILTER_IPX_DST_IPXNET,
292     FILTER_IPX_DST_IPXNODE,
293     FILTER_IPX_DST_IPXSOCK,
294     FILTER_IPX_SRC_IPXNET,
295     FILTER_IPX_SRC_IPXNODE,
296     FILTER_IPX_SRC_IPXSOCK
297 } FilterTokens;
298
299
300 static KeywordStruct filterKeywords[] = {
301     { "ip",     FILTER_IP_TYPE },
302     { "generic",FILTER_GENERIC_TYPE },
303     { "in",     FILTER_IN },
304     { "out",    FILTER_OUT },
305     { "forward",FILTER_FORWARD },
306     { "drop",   FILTER_DROP },
307     { "dstip",  FILTER_IP_DST },
308     { "srcip",  FILTER_IP_SRC },
309     { "dstport",FILTER_IP_DST_PORT },
310     { "srcport",FILTER_IP_SRC_PORT },
311     { "est",    FILTER_EST },
312     { "more",   FILTER_MORE },
313     { "!=",     FILTER_GENERIC_COMPNEQ },
314     { "==",     FILTER_GENERIC_COMPEQ  },
315     { "ipx",    FILTER_IPX_TYPE  },
316     { "dstipxnet",      FILTER_IPX_DST_IPXNET  },
317     { "dstipxnode",     FILTER_IPX_DST_IPXNODE  },
318     { "dstipxsock",     FILTER_IPX_DST_IPXSOCK  },
319     { "srcipxnet",      FILTER_IPX_SRC_IPXNET  },
320     { "srcipxnode",     FILTER_IPX_SRC_IPXNODE  },
321     { "srcipxsock",     FILTER_IPX_SRC_IPXSOCK  },
322     {  NULL , NO_TOKEN },
323 };
324
325 #define FILTER_DIRECTION        0
326 #define FILTER_DISPOSITION      1
327 #define IP_FILTER_COMPLETE      0x3     /* bits shifted by FILTER_DIRECTION */
328                                         /* FILTER_DISPOSITION */
329
330 #define IPX_FILTER_COMPLETE      0x3     /* bits shifted by FILTER_DIRECTION */
331                                         /* FILTER_DISPOSITION */
332
333 #define GENERIC_FILTER_COMPLETE 0x1c3   /* bits shifted for FILTER_DIRECTION */
334                                         /* FILTER_DISPOSITION, FILTER_GENERIC_OFFSET*/
335                                         /* FILTER_GENERIC_MASK, FILTER_GENERIC_VALUE*/
336
337     /*
338      * FilterProtoName:
339      *
340      * Ascii name of protocols used for filtering.
341      *
342      */
343 static KeywordStruct filterProtoName[] = {
344     { "tcp",  6 },
345     { "udp",  17 },
346     { "ospf", 89 },
347     { "icmp", 1 },
348     {  NULL , NO_TOKEN },
349 };
350
351 static KeywordStruct filterCompare[] = {
352     { ">", RAD_COMPARE_GREATER },
353     { "=", RAD_COMPARE_EQUAL },
354     { "<", RAD_COMPARE_LESS },
355     { "!=", RAD_COMPARE_NOT_EQUAL },
356     {  NULL , NO_TOKEN },
357 };
358
359 static char     curString[512];
360
361 static int findKey ( const char *string, KeywordStruct *list );
362 static int isAllDigit ( const char *token );
363 static short a2octet ( const char *tok, char *retBuf );
364 static char defaultNetmask ( uint32_t address );
365 static int ipAddressStringToValue ( char *string, uint32_t *ipAddress,
366                                          char *netmask);
367 static int parseIpFilter ( RadFilter *curEntry );
368 static int parseGenericFilter ( RadFilter *curEntry );
369 static int parseIpxFilter ( RadFilter *curEntry );
370 static int stringToNode   ( unsigned char* dest,  unsigned char* src );
371
372     /*
373      * findKey:
374      *
375      * Given a table of keywords, it will try and match string to an
376      * entry. If it does it returns that keyword value. if no NO_TOKEN is
377      * returned. A sanity check is made for upper case characters.
378      *
379      *  string:                 Pointer to the token to match.
380      *
381      *  list:                   Point to the list of keywords.
382      *
383      *  returns:                Keyword value on a match or NO_TOKEN.
384      */
385 int findKey(const char *string, KeywordStruct *list)
386 {
387     short       len;
388     KeywordStruct*  entry;
389     char        buf[80], *ptr;
390
391     len = strlen( string );
392     for( ptr = buf ; len; len--, string++ ) {
393         if( isupper( *string ) ) {
394             *ptr++ = tolower( *string );
395         } else {
396             *ptr++ = *string;
397         }
398     }
399     *ptr = 0;
400     entry = list;
401     while( entry->name ) {
402         if( strcmp( entry->name, buf ) == 0 ) {
403             break;
404         }
405         entry++;
406     }
407     return( entry->value );
408 }
409
410     /*
411      * isAllDigit:
412      *
413      * Routine checks a string to make sure all values are digits.
414      *
415      *  token:                  Pointer to sting to check.
416      *
417      *  returns:                TRUE if all digits, or FALSE.
418      *
419      */
420
421 static int
422 isAllDigit(const char *token)
423 {
424     int i;
425
426     i = strlen( token );
427     while( i-- ) {
428         if( isdigit( *token ) ) {
429             token++;
430         } else {
431             break;
432         }
433     }
434     if( i > 0 ) {
435         return( FALSE );
436     } 
437
438     return( TRUE );
439 }
440
441     /*
442      * a2octet:
443      *
444      * Converts the ascii mask and value for generic filters into octets.
445      * It also does a sanity check to see if the string is greater than
446      * MAX_FILTER_LEN. It assumes the sting is hex with NO leading "0x"
447      *
448      *  tok:                    Pointer to the string.
449      *
450      *  retBuf:                 Pointer to place the octets.
451      *
452      *  returns:                Number of octects or -1 for error.
453      * 
454      */
455 static short
456 a2octet(const char *tok, char *retBuf)
457 {
458     short       rc, len, val, retLen, i;
459     char        buf[ RAD_MAX_FILTER_LEN *2 ];
460     char        *octet = buf;
461
462     rc = -1;
463     retLen = 0;
464
465     if( ( len = strlen( tok ) ) <= ( RAD_MAX_FILTER_LEN*2 ) ) {
466         retLen = len/2;
467         if( len % 2 ) {
468             retLen++;
469         }
470         memset( buf, '\0', RAD_MAX_FILTER_LEN * 2 );
471         for( ; len; len-- ) {
472             if( *tok <= '9' && *tok >= '0' ) {
473                 val = '0';
474                 *octet++ = *tok++ - val;
475             } else if( isxdigit( *tok ) ) {
476                 if( *tok > 'Z' ) {
477                     val = 'a';
478                 } else {
479                     val = 'A';
480                 }
481                 *octet++ = ( *tok++ - val ) + 10;
482             } else {
483                 break;  
484             }
485         }
486         if( !len ) {
487             /* merge the values */
488             for( i = 0; i < RAD_MAX_FILTER_LEN*2; i+=2 ) {
489                 *retBuf++ = (buf[i] << 4) | buf[i+1];
490             }
491         }
492     }
493
494     if( len ) {
495         rc = -1;
496     } else {
497         rc = retLen;
498     }
499     return( rc );
500 }
501
502
503
504     /*
505      * defaultNetmask:
506      *
507      *  Given an ip address this routine calculate a default netmask.
508      *
509      *  address:                Ip address.
510      *
511      *  returns:                Number of bits for the netmask
512      *
513      */
514 static char
515 defaultNetmask(address)
516 uint32_t        address;
517 {
518     char netmask;
519
520     if ( ! address ) {
521         netmask = 0;
522     } else if (( address & htonl( 0x80000000 ) ) == 0 ) {
523         netmask = 8;
524     } else if (( address & htonl( 0xc0000000 ) ) == htonl( 0x80000000 ) ) {
525         netmask = 16;
526     } else if (( address & htonl( 0xe0000000 ) ) == htonl( 0xc0000000 ) ) {
527         netmask = 24;
528     } else {
529         netmask = 32;
530     }
531     return netmask;
532 }
533
534                 
535 static char ipAddressDigits[] = "1234567890./";
536     /*
537      * This functions attempts to convert an IP address in ASCII dot
538      * with an optional netmask part to a pair of IpAddress.  Note:
539      * An IpAddress is always stored in network byte order.
540      *
541      * Parameters:
542      *
543      *  string:         Pointer to a NULL terminated IP address in dot 
544      *                  notation followed by an optional /nn to indicate
545      *                  the number leading of bits in the netmask.
546      * 
547      *  ipAddress:      Pointer to an IpAddress where the converted
548      *                  address will be stored.
549      *
550      *  netmask:        Pointer to an IpAddress where the netmask
551      *                  will be stored.  If no netmask is passed as
552      *                  as part of the address the default netmask will
553      *                  be stored here.
554      *
555      * Returns:
556      *  <>              TRUE if valid conversion, FALSE otherwise.
557      *
558      *  *ipAddress:     If function returns TRUE, the IP address in NBO.
559      *  *netmask:       If function returns TRUE, the netmask in NBO.
560      */
561
562 static int
563 ipAddressStringToValue(char *string, uint32_t *ipAddress,
564         char *netmask)
565 {
566     u_char*     dst;
567     char*       cp;
568     int         numDots;
569     int         i;
570     long        value;
571
572     if ( ! string ) {
573         return(FALSE);
574     }
575
576     /* Allow an IP address to be blanked instead of forcing entry of
577        0.0.0.0 -- the user will like it. */
578
579     if ( *string == 0 ) {
580         *ipAddress = 0;
581         *netmask = 0;
582         return TRUE;
583     }
584
585     /* First just count the number of dots in the address.  If there
586        are more or less than three the address is invalid. */
587
588     cp = string;
589     numDots = 0;
590     while( *cp ) {
591         if( !strchr( ipAddressDigits, *cp) ) {
592             return( FALSE );
593         }
594         if ( *cp == '.') {
595             ++numDots;
596         }
597         ++cp;
598     }
599     if ( numDots != 3 ) {
600         return( FALSE );
601     }
602
603     dst = (u_char *) ipAddress;
604     cp = string;
605
606     for ( i = 0; i < sizeof( *ipAddress ); i++ ) {
607         value = strtol( cp, &cp, 10 );
608         if (( value < 0 ) || ( value > 255 )) {
609             return( FALSE );
610         }
611         *dst++ = (u_char) value;
612         if ( *cp == '.' ) {
613             cp += 1;
614         }
615     }
616
617     /* If there is a netmask part, parse it, otherwise figure out the
618        default netmask for this class of address. */
619
620     if ( *cp == '/' ) {
621         value = strtol( cp + 1, &cp, 10 );
622         if (( *cp != 0 ) || ( value < 0 ) || ( value > 32 )) {
623             return FALSE;
624         }
625         *netmask = (char) value;
626     } else {
627         *netmask = defaultNetmask( *ipAddress );
628     }
629     return TRUE;
630 }
631
632     /*
633      * Convert a 12 digit string representation of a hex data field to a
634      * value.
635      */
636 static int
637 stringToNode(dest, src )
638 unsigned char*  dest;
639 unsigned char*  src;
640 {
641     int         srcIx = 0;
642     int         ix;
643     int         nibble1;
644     int         nibble2;
645     int         temp;
646     unsigned char *src1;
647
648     src1 = (unsigned char *) strchr(src, 'x');
649
650     if (src1 == NULL)
651         src1 = (unsigned char *) strchr(src,'X');
652
653     if (src1 == NULL)
654         src1 = src;
655     else
656         src1++;
657
658     /* skip any leading 0x or 0X 's */
659     temp = strlen( (char*) src1 );
660     if( strlen( (unsigned char*) src1 ) != ( IPX_NODE_ADDR_LEN * 2 ) ) {
661         return( FALSE );
662     }
663
664     for ( ix = 0; ix < IPX_NODE_ADDR_LEN; ++ix ) {
665         if ( src1[ srcIx ] <= '9' ) {
666             nibble1 = src1[ srcIx ] & 0x0f;
667         } else {
668             nibble1 = (src1[ srcIx ] & 0x0f) + 9;
669         }
670         srcIx += 1;
671         if ( src1[ srcIx ] <= '9' ) {
672             nibble2 = src1[ srcIx ] & 0x0f;
673         } else {
674             nibble2 = (src1[ srcIx ] & 0x0f) + 9;
675         }
676         srcIx += 1;
677         ((unsigned char *) dest)[ ix ] = (unsigned char) (nibble1 << 4) + nibble2;
678     }
679
680     return( TRUE );
681 }
682
683
684     /*
685      * parseIpxFilter:
686      *
687      * This routine parses an IPX filter string from a RADIUS
688      * reply. The format of the string is:
689      *
690      *  ipx dir action [ srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]]
691      *                 [ dstipxnet nnnn dstipxnode mmmmm [dstipxsoc cmd value ]]
692      *
693      * Fields in [...] are optional.
694      *  where:
695      *
696      *  ipx:            Keyword to designate an IPX filter. Actually this
697      *                  has been determined by parseFilter.
698      *
699      *  dir:            Filter direction. "IN" or "OUT"
700      *
701      *  action:         Filter action. "FORWARD" or "DROP"
702      *
703      *  srcipxnet:      Keyword for source IPX address.
704      *                  nnnn = IPX Node address.
705      *
706      *  srcipxnode:     Keyword for source IPX Node address.
707      *                  mmmmm = IPX Node Address, could be FFFFFF.
708      *                  A vlid ipx node number should accompany ipx net number.
709      *
710      *  srcipxsoc:      Keyword for source IPX socket address.
711      *
712      *  cmd:            One of ">" or "<" or "=" or "!=".
713      *
714      *  value:          Socket value to be compared against, in hex. 
715      *                  
716      *  dstipxnet:      Keyword for destination IPX address.
717      *                  nnnn = IPX Node address. 
718      *                  
719      *  dstipxnode:     Keyword for destination IPX Node address.
720      *                  mmmmm = IPX Node Address, could be FFFFFF.
721      *                  A vlid ipx node number should accompany ipx net number.
722      *                  
723      *  dstipxsoc:      Keyword for destination IPX socket address.
724      *                  
725      *  cmd:            One of ">" or "<" or "=" or "!=".
726      *                  
727      *  value:          Socket value to be compared against, in hex.            
728      *                  
729      *                  
730      * expects:
731      *
732      *  curEntry:       Pointer to place the filter structure
733      *
734      *  returns:        -1 for error or 0 for OK
735      *  
736      */
737
738 static int 
739 parseIpxFilter(curEntry)
740 RadFilter       *curEntry;
741 {
742     unsigned long       elements = 0l;
743     int                 tok; 
744     char*               token;
745     RadIpxFilter*       ipx;
746
747     token = strtok( NULL, " " ); 
748
749     memset( curEntry, '\0', sizeof( RadFilter ) );
750     curEntry->type = RAD_FILTER_IPX; 
751     ipx = &curEntry->u.ipx;
752  
753     while( token ) {
754         tok = findKey( token, filterKeywords );
755         switch( tok ) {
756             case FILTER_IN:
757             case FILTER_OUT:
758                 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
759                 elements |= (1 << FILTER_DIRECTION );
760                 break;
761
762             case FILTER_FORWARD:
763             case FILTER_DROP:
764                 elements |= (1 << FILTER_DISPOSITION );
765                 if( tok == FILTER_FORWARD ) {
766                     curEntry->forward = TRUE;
767                 } else {
768                     curEntry->forward = FALSE;
769                 }
770                 break;
771
772             case FILTER_IPX_DST_IPXNET:
773             case FILTER_IPX_SRC_IPXNET:
774                 token = strtok( NULL, " " );
775
776                 if ( token ) {
777                     if( tok == FILTER_IPX_DST_IPXNET ) {
778                         ipx->dstIpxNet = ntohl( strtol( token, 0, 16 ));
779                     } else {
780                         ipx->srcIpxNet = ntohl( strtol( token, 0, 16 ));
781                     }
782                     break;
783                 } 
784                 goto doneErr; 
785
786             case FILTER_IPX_DST_IPXNODE:
787             case FILTER_IPX_SRC_IPXNODE:
788                 token = strtok( NULL, " " );
789
790                 if ( token ) {
791                     if ( tok == FILTER_IPX_DST_IPXNODE) {
792                         stringToNode( (unsigned char *)ipx->dstIpxNode, (unsigned char*)token );
793                     } else {
794                         stringToNode( (unsigned char *)ipx->srcIpxNode, (unsigned char*)token );
795                     }
796                     break;
797                 }
798                 goto doneErr;
799
800             case FILTER_IPX_DST_IPXSOCK:
801             case FILTER_IPX_SRC_IPXSOCK:
802             {
803                 RadFilterComparison cmp;
804
805                 token = strtok( NULL, " " );
806
807                 if ( token ) {
808                     cmp = findKey( token, filterCompare );
809                     if( cmp != NO_TOKEN ) {
810                     token = strtok( NULL, " " );
811                         if ( token ) {
812                             if ( tok == FILTER_IPX_DST_IPXSOCK ) {
813                                 ipx->dstSocComp = cmp;
814                                 ipx->dstIpxSoc = 
815                             ntohs( (IpxSocket) strtol( token, NULL, 16 ));
816                             } else {
817                                 ipx->srcSocComp = cmp;
818                                 ipx->srcIpxSoc 
819                                     = ntohs( (IpxSocket) strtol( token, NULL, 16 ));
820                             }
821                             break;
822                         }
823                     }
824                 }
825                 goto doneErr;
826              }
827
828             default:
829                 /* no keyword match */
830                 goto doneErr;
831         }
832         token = strtok( NULL, " " ); 
833     } 
834
835     if( elements == IPX_FILTER_COMPLETE ) {
836         return( 0 );
837     }
838
839 doneErr:
840     librad_log("ipx filter error: do not recognize %s in %s \n",
841               token, curString );
842     return( -1 );
843 }
844
845     /*
846      * parseIpFilter:
847      *
848      * This routine parses an IP filter string from a RADIUS
849      * reply. The format of the string is:
850      *
851      *  ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
852      *      [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ] 
853      *
854      * Fields in [...] are optional.
855      *  where:
856      *
857      *  ip:             Keyword to designate an IP filter. Actually this
858      *                  has been determined by parseFilter.
859      *
860      *  dir:            Filter direction. "IN" or "OUT"
861      *
862      *  action:         Filter action. "FORWARD" or "DROP"
863      *
864      *  dstip:          Keyword for destination IP address.
865      *                  n.n.n.n = IP address. /nn - netmask. 
866      *                  
867      *  srcip:          Keyword for source IP address.
868      *                  n.n.n.n = IP address. /nn - netmask. 
869      *                  
870      *  proto:          Optional protocol field. Either a name or
871      *                  number. Known names are in FilterProtoName[].
872      *                  
873      *  dstpost:        Keyword for destination port. Only valid with tcp
874      *                  or udp. 'cmp' are in FilterPortType[]. 'value' can be
875      *                  a name or number.
876      *
877      *  srcpost:        Keyword for source port. Only valid with tcp
878      *                  or udp. 'cmp' are in FilterPortType[]. 'value' can be
879      *                  a name or number.
880      *                  
881      *  est:            Keyword for TCP established. Valid only for tcp.
882      *                  
883      * expects:
884      *
885      *  curEntry:       Pointer to place the filter structure
886      *
887      *  returns:        -1 for error or 0 for OK
888      *  
889      */
890
891 static int 
892 parseIpFilter(curEntry)
893 RadFilter       *curEntry;
894 {
895  
896     unsigned long       elements = 0l;
897     int                 tok; 
898     char*               token;
899     RadIpFilter*        ip;
900
901     token = strtok( NULL, " " ); 
902
903     memset( curEntry, '\0', sizeof( RadFilter ) );
904     curEntry->type = RAD_FILTER_IP; 
905     ip = &curEntry->u.ip;
906     ip->established = FALSE;
907  
908     while( token ) {
909         tok = findKey( token, filterKeywords );
910         switch( tok ) {
911             case FILTER_IN:
912             case FILTER_OUT:
913                 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
914                 elements |= (1 << FILTER_DIRECTION );
915                 break;
916             case FILTER_FORWARD:
917             case FILTER_DROP:
918                 elements |= (1 << FILTER_DISPOSITION );
919                 if( tok == FILTER_FORWARD ) {
920                     curEntry->forward = TRUE;
921                 } else {
922                     curEntry->forward = FALSE;
923                 }
924                 break;
925             case FILTER_IP_DST:
926             case FILTER_IP_SRC:
927                 token = strtok( NULL, " " );
928                 if ( token ) {
929                     if( tok == FILTER_IP_DST ) {
930                         
931                         if( ipAddressStringToValue( token, 
932                                  &ip->dstip, (char *)&ip->dstmask ) ) {
933                             break;
934                         }
935                     } else {
936                         if( ipAddressStringToValue( token, 
937                                 &ip->srcip, (char *)&ip->srcmask ) ) {
938                             break;
939                         }
940                     }
941                 } 
942
943                 librad_log("ip filter error: do not recognize %s in %s \n",
944                           token, curString );
945                 goto doneErr ;
946
947             case FILTER_IP_DST_PORT:
948             case FILTER_IP_SRC_PORT:
949             {
950                 RadFilterComparison cmp;
951                 short            port;
952
953                 token = strtok( NULL, " " );
954                 if ( token ) {
955                     cmp = findKey( token, filterCompare );
956                     if( cmp != NO_TOKEN ) {
957                         token = strtok( NULL, " " );
958                         if ( token ) {
959                             if( isAllDigit( token ) ) {
960                                 port = atoi( (char *) token );
961                             } else {
962                                 port = findKey( token, filterPortType );
963                             }
964                             if( port != (short) NO_TOKEN ) {
965                                 if( tok == FILTER_IP_DST_PORT ) {
966                                     ip->dstPortComp = cmp;
967                                     ip->dstport = htons( port );
968                                 } else {
969                                     ip->srcPortComp = cmp;
970                                     ip->srcport = htons( port );
971                                 }
972                                 break;
973                             }
974                         }
975                     }
976                 }
977                 librad_log( "ip filter error: do not recognize %s in %s \n",
978                           token, curString );
979                 goto doneErr;
980                 break;
981             }
982             case FILTER_EST:
983                 ip->established = TRUE;
984                 break;
985             default:
986                 /* no keyword match but may match a protocol list */
987                 if( isAllDigit( token ) ) {
988                     tok = atoi( (char *) token );
989                 } else {
990                     tok = findKey( token, filterProtoName );
991
992                     if( tok == NO_TOKEN ) {
993                         librad_log("ip filter error: do not recognize %s in %s \n",
994                              token, curString );
995                         goto doneErr;
996                     }
997                 }
998                 ip->proto = tok;
999         }
1000         token = strtok( NULL, " " ); 
1001     } 
1002
1003     if( elements == IP_FILTER_COMPLETE ) {
1004         return( 0 );
1005     }
1006
1007 doneErr:
1008     return( -1 );
1009 }
1010
1011     /*
1012      * parseGenericFilter:
1013      *
1014      * This routine parses a Generic filter string from a RADIUS
1015      * reply. The format of the string is:
1016      *
1017      *  GENERIC dir action offset mask value [== or != ] [more]
1018      *
1019      * Fields in [...] are optional.
1020      *  where:
1021      *
1022      *  generic:        Keyword to indicate a generic filter. This
1023      *                  has been determined by parseFilter.
1024      *
1025      *  dir:            Filter direction. "IN" or "OUT"
1026      *
1027      *  action:         Filter action. "FORWARD" or "DROP"
1028      *
1029      *  offset:         A Number. Specifies an offset into a frame 
1030      *                  to start comparing.
1031      *                  
1032      *  mask:           A hexadecimal mask of bits to compare.
1033      *                  
1034      *  value:          A value to compare with the masked data.
1035      *
1036      *  compNeq:        Defines type of comparison. ( "==" or "!=")
1037      *                  Default is "==".
1038      *                  
1039      *  more:           Optional keyword MORE, to represent the attachment
1040      *                  to the next entry.
1041      *
1042      * expects:
1043      *
1044      *  curEntry:       Pointer to place the filter structure
1045      *
1046      *  returns:        -1 for error or 0 for OK
1047      *  
1048      */
1049
1050 static int
1051 parseGenericFilter(curEntry)
1052 RadFilter       *curEntry;
1053 {
1054     unsigned long       elements = 0l; 
1055     int                 tok; 
1056     int                 gstate = FILTER_GENERIC_OFFSET;
1057     char*               token;
1058     short               valLen, maskLen;
1059     RadGenericFilter*   gen;
1060
1061     token = strtok( NULL, " " ); 
1062
1063     maskLen = 0;
1064     memset( (char *)curEntry, '\0', sizeof( RadFilter ) );
1065     curEntry->type = RAD_FILTER_GENERIC;
1066     gen = &curEntry->u.generic;
1067     gen->more = FALSE; 
1068     gen->compNeq = FALSE;       
1069
1070     while( token ) {
1071         tok = findKey( token, filterKeywords );
1072         switch( tok ) {
1073             case FILTER_IN:
1074             case FILTER_OUT:
1075                 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
1076                 elements |= (1 << FILTER_DIRECTION );
1077                 break;
1078             case FILTER_FORWARD:
1079             case FILTER_DROP:
1080                 elements |= (1 << FILTER_DISPOSITION );
1081                 if( tok == FILTER_FORWARD ) {
1082                     curEntry->forward = TRUE;
1083                 } else {
1084                     curEntry->forward = FALSE;
1085                 }
1086                 break;
1087             case FILTER_GENERIC_COMPNEQ:
1088                 gen->compNeq = TRUE;
1089                 break;
1090             case FILTER_GENERIC_COMPEQ:
1091                 gen->compNeq = FALSE;
1092                 break;
1093             case FILTER_MORE:
1094                 gen->more = htons( TRUE );
1095                 break;
1096             default:
1097                 elements |= ( 1 << gstate );
1098                 switch( gstate ) {
1099                     case FILTER_GENERIC_OFFSET:
1100                         gstate = FILTER_GENERIC_MASK;
1101                         gen->offset = htons( atoi( (char *) token ) );
1102                         break;
1103                     case FILTER_GENERIC_MASK:
1104                         gstate = FILTER_GENERIC_VALUE;
1105                         maskLen = a2octet( token, (char *)gen->mask );
1106                         if( maskLen == (short) -1 ) {
1107                             librad_log("filter mask error: %s \n", curString );
1108                             goto doneErr;
1109                         }
1110                         break;
1111                     case FILTER_GENERIC_VALUE:
1112                         gstate ++;
1113                         valLen = a2octet( token, (char *)gen->value );
1114                         if( valLen != maskLen ) {
1115                             librad_log("filter value size is not the same size as the filter mask: %s \n", 
1116                                      curString );
1117                             goto doneErr;
1118                         }
1119                         gen->len = htons( valLen );
1120                         break;
1121                     default:
1122                         librad_log("filter: do not know %s in %s \n",
1123                                  token, curString );
1124                         goto doneErr;    
1125                 }
1126         }
1127         token = strtok( NULL, " " ); 
1128     }
1129
1130     if( elements == GENERIC_FILTER_COMPLETE ) {
1131         return( 0 );
1132     }
1133
1134 doneErr:
1135     return( -1 );
1136 }
1137                        
1138     /*
1139      * filterBinary:
1140      *
1141      * This routine will call routines to parse entries from an ASCII format
1142      * to a binary format recognized by the Ascend boxes.
1143      *
1144      *  pair:                   Pointer to value_pair to place return.
1145      *
1146      *  valstr:                 The string to parse     
1147      *
1148      *  return:                 -1 for error or 0.
1149      */
1150 int 
1151 filterBinary(VALUE_PAIR *pair, const char *valstr)
1152 {
1153
1154     char*               token;
1155     unsigned long       tok;
1156     int                 rc;
1157     RadFilter           radFil, *filt;
1158     RadGenericFilter*   gen;
1159     static VALUE_PAIR   *prevRadPair = NULL;
1160     char                *copied_valstr;
1161
1162     rc = -1;
1163     strcpy( curString, valstr );
1164
1165     /*
1166      *  Copy the valstr, so we don't smash it in place via strtok!
1167      */
1168     copied_valstr = strdup(valstr);
1169     if (!copied_valstr) return -1;
1170
1171     token = strtok(copied_valstr, " " );
1172     tok = findKey( token, filterType );
1173     pair->length = SIZEOF_RADFILTER;
1174     switch( tok ) {
1175       case RAD_FILTER_GENERIC:
1176         rc = parseGenericFilter( &radFil );
1177         break;
1178       case RAD_FILTER_IP:
1179         rc = parseIpFilter( &radFil );
1180         break;
1181       case RAD_FILTER_IPX:
1182         rc = parseIpxFilter( &radFil );
1183         break;
1184       default:
1185         librad_log("filterBinary: unknown filter type \"%s\"", token);
1186         free(copied_valstr);
1187         return -1;
1188         break;
1189     }
1190     free(copied_valstr);
1191     copied_valstr = NULL;
1192
1193     /*
1194      * if 'more' is set then this new entry must exist, be a 
1195      * FILTER_GENERIC_TYPE, direction and disposition must match for 
1196      * the previous 'more' to be valid. If any should fail then TURN OFF 
1197      * previous 'more'
1198      */
1199     if( prevRadPair ) {
1200         filt = ( RadFilter * )prevRadPair->strvalue;
1201         if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
1202            ( prevRadPair->attribute != pair->attribute ) || 
1203            ( filt->indirection != radFil.indirection ) || 
1204            ( filt->forward != radFil.forward ) ) {
1205             gen = &filt->u.generic;
1206             gen->more = FALSE;
1207             librad_log("filterBinary:  'more' for previous entry doesn't match: %s.\n",
1208                      curString );
1209         }
1210     }
1211     prevRadPair = NULL;
1212     if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
1213         if( radFil.u.generic.more ) {
1214             prevRadPair = pair;
1215         } 
1216     }
1217
1218     if( rc != -1 ) {
1219         memcpy( pair->strvalue, (char *) &radFil, pair->length );
1220     }
1221     return(rc);
1222 }
1223
1224
1225 /********************************************************************/
1226
1227 /*
1228  *  The following code was written specifically for the FreeRADIUS
1229  *  server by Alan DeKok <aland@ox.org>, and as such, falls under
1230  *  the GPL, and not under the previous Ascend license.
1231  */
1232
1233 static const char *FindValue(int value, KeywordStruct *list)
1234 {
1235   KeywordStruct *entry;
1236
1237   entry = list;
1238   while (entry->name) {
1239     if (entry->value == value) {
1240       return entry->name;
1241     }
1242     entry++;
1243   }
1244
1245   return "???";
1246 }
1247
1248 /*
1249  *      Print an Ascend binary filter attribute to a string,
1250  *      Grrr... Ascend makes the server do this work, instead
1251  *      of doing it on the NAS.
1252  *
1253  *      Note we don't bother checking 'len' after the snprintf's.
1254  *      This function should ONLY be called with a large (~1k) buffer.
1255  */
1256 void print_abinary(VALUE_PAIR *vp, u_char *buffer, int len)
1257 {
1258   int i;
1259   char *p;
1260   RadFilter     filter;
1261   
1262   static const char *action[] = {"drop", "forward"};
1263   static const char *direction[] = {"output", "input"};
1264   
1265   p = buffer;
1266
1267   /*
1268    *  Just for paranoia: wrong size filters get printed as octets
1269    */
1270   if (vp->length != SIZEOF_RADFILTER) {
1271     strcpy(p, "0x");
1272     p += 2;
1273     for (i = 0; i < vp->length; i++) {
1274       sprintf(p, " %02x", vp->strvalue[i]);
1275       p += 3;
1276     }
1277     return;
1278   }
1279
1280   memcpy(&filter, vp->strvalue, SIZEOF_RADFILTER); /* alignment issues */
1281   *(p++) = '"';
1282   len -= 3;                     /* account for leading & trailing quotes */
1283
1284   i = snprintf(p, len, "%s %s %s",
1285                FindValue(filter.type, filterType),
1286                action[filter.forward & 0x01],
1287                direction[filter.indirection & 0x01]);
1288   p += i;
1289   len -= i;
1290
1291   /*
1292    *    Handle IP filters
1293    */
1294   if (filter.type == RAD_FILTER_IP) {
1295     if (filter.u.ip.dstip) {
1296       i = snprintf(p, len, " dstip %d.%d.%d.%d/%d",
1297                    ((u_char *) &filter.u.ip.dstip)[0],
1298                    ((u_char *) &filter.u.ip.dstip)[1],
1299                    ((u_char *) &filter.u.ip.dstip)[2],
1300                    ((u_char *) &filter.u.ip.dstip)[3],
1301                    filter.u.ip.dstmask);
1302       p += i;
1303       len -= i;
1304     }
1305     
1306     if (filter.u.ip.srcip) {
1307       i = snprintf(p, len, " srcip %d.%d.%d.%d/%d",
1308                    ((u_char *) &filter.u.ip.srcip)[0],
1309                    ((u_char *) &filter.u.ip.srcip)[1],
1310                    ((u_char *) &filter.u.ip.srcip)[2],
1311                    ((u_char *) &filter.u.ip.srcip)[3],
1312                    filter.u.ip.srcmask);
1313       p += i;
1314       len -= i;
1315     }
1316
1317     i =  snprintf(p, len, " %d", filter.u.ip.proto);
1318     p += i;
1319     len -= i;
1320     
1321     if (filter.u.ip.dstPortComp) {
1322       i = snprintf(p, len, " dstport %s %d",
1323                    FindValue(filter.u.ip.dstPortComp, filterCompare),
1324                    ntohs(filter.u.ip.dstport));
1325       p += i;
1326       len -= i;
1327     }
1328     
1329     if (filter.u.ip.srcPortComp) {
1330       i = snprintf(p, len, " srcport %s %d",
1331                    FindValue(filter.u.ip.srcPortComp, filterCompare),
1332                    ntohs(filter.u.ip.srcport));
1333       p += i;
1334       len -= i;
1335     }
1336
1337     /*
1338      *  Handle IPX filters
1339      */
1340   } else if (filter.type == RAD_FILTER_IPX) {
1341     /* print for source */
1342     if (filter.u.ipx.srcIpxNet) {
1343       i = snprintf(p, len, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
1344                   ntohl(filter.u.ipx.srcIpxNet),
1345                   filter.u.ipx.srcIpxNode[0], filter.u.ipx.srcIpxNode[1], 
1346                   filter.u.ipx.srcIpxNode[2], filter.u.ipx.srcIpxNode[3], 
1347                   filter.u.ipx.srcIpxNode[4], filter.u.ipx.srcIpxNode[5]);
1348       p += i;
1349       len -= i;
1350
1351       if (filter.u.ipx.srcSocComp) {
1352         i = snprintf(p, len, " srcipxsock %s 0x%04x",
1353                      FindValue(filter.u.ipx.srcSocComp, filterCompare),
1354                      ntohs(filter.u.ipx.srcIpxSoc));
1355         p += i;
1356         len -= i;
1357       }
1358     }
1359
1360     /* same for destination */
1361     if (filter.u.ipx.dstIpxNet) {
1362       i = snprintf(p, len, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
1363                   ntohl(filter.u.ipx.dstIpxNet),
1364                   filter.u.ipx.dstIpxNode[0], filter.u.ipx.dstIpxNode[1], 
1365                   filter.u.ipx.dstIpxNode[2], filter.u.ipx.dstIpxNode[3], 
1366                   filter.u.ipx.dstIpxNode[4], filter.u.ipx.dstIpxNode[5]);
1367       p += i;
1368       len -= i;
1369
1370       if (filter.u.ipx.dstSocComp) {
1371         i = snprintf(p, len, " dstipxsock %s 0x%04x",
1372                      FindValue(filter.u.ipx.dstSocComp, filterCompare),
1373                      ntohs(filter.u.ipx.dstIpxSoc));
1374         p += i;
1375         len -= i;
1376       }
1377     }
1378
1379
1380   } else if (filter.type == RAD_FILTER_GENERIC) {
1381     int count;
1382
1383     i = snprintf(p, len, " %d ", filter.u.generic.offset);
1384     p += i;
1385     i -= len;
1386
1387     /* show the mask */
1388     for (count = 0; count < ntohs(filter.u.generic.len); count++) {
1389       i = snprintf(p, len, "%02x", filter.u.generic.mask[count]);
1390       p += i;
1391       len -= i;
1392     }
1393
1394     strcpy(p, " ");
1395     p++;
1396     len--;
1397
1398     /* show the value */
1399     for (count = 0; count < ntohs(filter.u.generic.len); count++) {
1400       i = snprintf(p, len, "%02x", filter.u.generic.value[count]);
1401       p += i;
1402       len -= i;
1403     }
1404
1405     i = snprintf(p, len, " %s", (filter.u.generic.compNeq) ? "!=" : "==");
1406     p += i;
1407     len -= i;
1408   }
1409
1410   *(p++) = '"';
1411   *p = '\0';
1412 }