Jumbo fix to quiet compiler warnings by adding (int) cast to
[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 <string.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28
29 #include "libradius.h"
30
31 #if HAVE_NETINET_IN_H
32 #include <netinet/in.h>
33 #endif
34
35 #define NO_TOKEN -1
36
37 /*
38  * Two types of filters are supported, GENERIC and IP.  The identifiers
39  * are:
40  */
41
42 #define RAD_FILTER_GENERIC      0
43 #define RAD_FILTER_IP           1
44 #define RAD_FILTER_IPX          2
45
46 /*
47  * Generic filters mask and match up to RAD_MAX_FILTER_LEN bytes
48  * starting at some offset.  The length is:
49  */
50 #define RAD_MAX_FILTER_LEN      6
51
52 /*
53  * ASCEND extensions for ABINARY filters
54  */
55
56 #define IPX_NODE_ADDR_LEN               6
57
58 typedef uint32_t                IpxNet;
59 typedef unsigned char           IpxNode[ IPX_NODE_ADDR_LEN ];
60 typedef unsigned short          IpxSocket;
61
62 #if ! defined( FALSE )
63 # define FALSE          0
64 # define TRUE           (! FALSE)
65 #endif
66
67 /*
68  * RadFilterComparison:
69  *
70  * An enumerated values for the IP filter port comparisons.
71  */
72 typedef enum {
73         RAD_NO_COMPARE,
74         RAD_COMPARE_LESS,
75         RAD_COMPARE_EQUAL,
76         RAD_COMPARE_GREATER,
77         RAD_COMPARE_NOT_EQUAL
78 } RadFilterComparison;
79
80     /*
81      * RadIpFilter:
82      *
83      * The binary format of an IP filter.  ALL fields are stored in
84      * network byte order.
85      *
86      *  srcip:          The source IP address.
87      *
88      *  dstip:          The destination IP address.
89      *
90      *  srcmask:        The number of leading one bits in the source address
91      *                  mask.  Specifies the bits of interest.
92      *
93      *  dstmask:        The number of leading one bits in the destination
94      *                  address mask. Specifies the bits of interest.
95      *
96      *  proto:          The IP protocol number
97      *
98      *  establised:     A boolean value.  TRUE when we care about the
99      *                  established state of a TCP connection.  FALSE when
100      *                  we dont care.
101      *
102      *  srcport:        TCP or UDP source port number.
103      *
104      *  dstport:        TCP or UDP destination port number.
105      *
106      *  srcPortCmp:     One of the values of the RadFilterComparison enumeration
107      *                  specifying how to compare the srcport value.
108      *
109      *  dstPortCmp:     One of the values of the RadFilterComparison enumeration
110      *                  specifying how to compare the dstport value.
111      *
112      *  fill:           Round things out to a dword boundary.
113      */
114 typedef struct radip {
115     uint32_t            srcip;
116     uint32_t            dstip;
117     unsigned char       srcmask;
118     unsigned char       dstmask;
119     unsigned char       proto;
120     unsigned char       established;
121     unsigned short      srcport;
122     unsigned short      dstport;
123     unsigned char       srcPortComp;
124     unsigned char       dstPortComp;
125     unsigned char       fill[4];        /* used to be fill[2] */
126 } RadIpFilter;
127
128     /*
129      * RadIpxFilter:
130      * The binary format of a GENERIC filter.  ALL fields are stored in
131      * network byte order.
132      *
133      *  srcIpxNet:      Source IPX Net address
134      *
135      *  srcIpxNode:     Source IPX Node address
136      *
137      *  srcIpxSoc:      Source IPX socket address
138      *
139      *  dstIpxNet:      Destination IPX Net address
140      *
141      *  dstIpxNode:     Destination IPX Node address
142      *
143      *  dstIpxSoc:      Destination IPX socket address
144      *
145      *  srcSocComp:     Source socket compare value
146      *
147      *  dstSocComp:     Destination socket compare value
148      *
149      */
150 typedef struct radipx {                         
151     IpxNet              srcIpxNet;                      /* LongWord */
152     IpxNode             srcIpxNode;                     /* Byte[6] */
153     IpxSocket           srcIpxSoc;                      /* Word */
154     IpxNet              dstIpxNet;                      /* LongWord */
155     IpxNode             dstIpxNode;                     /* Byte[6] */
156     IpxSocket           dstIpxSoc;                      /* Word */
157     unsigned char       srcSocComp;
158     unsigned char       dstSocComp;
159 } RadIpxFilter;
160
161     /*
162      * RadGenericFilter:
163      *
164      * The binary format of a GENERIC filter.  ALL fields are stored in
165      * network byte order.
166      *
167      *  offset:         Number of bytes into packet to start comparison.
168      *
169      *  len:            Number of bytes to mask and compare.  May not
170      *                  exceed RAD_MAX_FILTER_LEN.
171      *
172      *  more:           Boolean.  If non-zero the next filter entry is
173      *                  also to be applied to a packet.
174      *
175      *  mask:           A bit mask specifying the bits to compare.
176      *
177      *  value:          A value to compare against the masked bits at
178      *                  offset in a users packet.
179      *                  
180      *  compNeq:        Defines type of comarison (Equal or Notequal)
181      *                  default is Equal.
182      *
183      *  fill:           Round things out to a dword boundary
184      */
185 typedef struct radgeneric {
186     unsigned short      offset;
187     unsigned short      len;
188     unsigned short      more;
189     unsigned char       mask[ RAD_MAX_FILTER_LEN ];
190     unsigned char       value[ RAD_MAX_FILTER_LEN ];
191     unsigned char       compNeq;
192     unsigned char       fill[3];        /* used to be fill */
193 } RadGenericFilter;
194
195     /*
196      * RadFilter:
197      *
198      * A binary filter element.  Contains either a RadIpFilter or a
199      * RadGenericFilter.  All fields are stored in network byte order.
200      *
201      *  type:           Either RAD_FILTER_GENERIC or RAD_FILTER_IP.
202      *
203      *  forward:        TRUE if we should forward packets that match this
204      *                  filter, FALSE if we should drop packets that match
205      *                  this filter.
206      *
207      *  indirection:    TRUE if this is an input filter, FALSE if this is
208      *                  an output filter.
209      *
210      *  fill:           Round things out to a dword boundary.
211      *
212      *  u:              A union of
213      *                  ip:             An ip filter entry
214      *                  generic:        A generic filter entry
215      */
216 typedef struct filter {
217     unsigned char       type;
218     unsigned char       forward;
219     unsigned char       indirection;
220     unsigned char       fill;
221     union {
222         RadIpFilter      ip;
223         RadIpxFilter     ipx;
224         RadGenericFilter generic;
225     } u;
226 } RadFilter;
227 #define SIZEOF_RADFILTER 32
228
229 typedef struct {
230     const char* name;
231     unsigned int        value;
232 } KeywordStruct;
233
234 /*
235  * FilterPortType:
236  *
237  * Ascii names of some well known tcp/udp services.
238  * Used for filtering on a port type.
239  *
240  * ??? What the heck is wrong with getservbyname?
241  */
242 static const 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 const 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_GENERIC_TYPE,
273     FILTER_IP_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 const 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  *  ??? What the heck is wrong with getprotobyname?
343  */
344 static const KeywordStruct filterProtoName[] = {
345     { "tcp",  6 },
346     { "udp",  17 },
347     { "ospf", 89 },
348     { "icmp", 1 },
349     { "0", 0 },
350     {  NULL , NO_TOKEN },
351 };
352
353 static const KeywordStruct filterCompare[] = {
354     { ">", RAD_COMPARE_GREATER },
355     { "=", RAD_COMPARE_EQUAL },
356     { "<", RAD_COMPARE_LESS },
357     { "!=", RAD_COMPARE_NOT_EQUAL },
358     {  NULL , NO_TOKEN },
359 };
360
361 static int findKey ( const char *string, const 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 ( const char *curString, RadFilter *curEntry );
368 static int parseGenericFilter ( const char *curString, RadFilter *curEntry );
369 static int parseIpxFilter ( const char *curString, 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, const KeywordStruct *list)
386 {
387     short       len;
388     const KeywordStruct*  entry;
389     char        buf[80], *ptr;
390
391     len = strlen( string );
392     for( ptr = buf ; len; len--, string++ ) {
393         if( isupper( (int) *string ) ) {
394             *ptr++ = tolower( (int) *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( (int) *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( (int) *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 const 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 < (int)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((char *)src, 'x');
649
650     if (src1 == NULL)
651         src1 = (unsigned char *) strchr((char *)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( (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(curString, curEntry)
740 const char      *curString;
741 RadFilter       *curEntry;
742 {
743     unsigned long       elements = 0l;
744     int                 tok; 
745     char*               token;
746     RadIpxFilter*       ipx;
747
748     token = strtok( NULL, " " ); 
749
750     memset( curEntry, '\0', sizeof( RadFilter ) );
751     curEntry->type = RAD_FILTER_IPX; 
752     ipx = &curEntry->u.ipx;
753  
754     while( token ) {
755         tok = findKey( token, filterKeywords );
756         switch( tok ) {
757             case FILTER_IN:
758             case FILTER_OUT:
759                 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
760                 elements |= (1 << FILTER_DIRECTION );
761                 break;
762
763             case FILTER_FORWARD:
764             case FILTER_DROP:
765                 elements |= (1 << FILTER_DISPOSITION );
766                 if( tok == FILTER_FORWARD ) {
767                     curEntry->forward = TRUE;
768                 } else {
769                     curEntry->forward = FALSE;
770                 }
771                 break;
772
773             case FILTER_IPX_DST_IPXNET:
774             case FILTER_IPX_SRC_IPXNET:
775                 token = strtok( NULL, " " );
776
777                 if ( token ) {
778                     if( tok == FILTER_IPX_DST_IPXNET ) {
779                         ipx->dstIpxNet = ntohl( strtol( token, 0, 16 ));
780                     } else {
781                         ipx->srcIpxNet = ntohl( strtol( token, 0, 16 ));
782                     }
783                     break;
784                 } 
785                 goto doneErr; 
786
787             case FILTER_IPX_DST_IPXNODE:
788             case FILTER_IPX_SRC_IPXNODE:
789                 token = strtok( NULL, " " );
790
791                 if ( token ) {
792                     if ( tok == FILTER_IPX_DST_IPXNODE) {
793                         stringToNode( (unsigned char *)ipx->dstIpxNode, (unsigned char*)token );
794                     } else {
795                         stringToNode( (unsigned char *)ipx->srcIpxNode, (unsigned char*)token );
796                     }
797                     break;
798                 }
799                 goto doneErr;
800
801             case FILTER_IPX_DST_IPXSOCK:
802             case FILTER_IPX_SRC_IPXSOCK:
803             {
804                 RadFilterComparison cmp;
805
806                 token = strtok( NULL, " " );
807
808                 if ( token ) {
809                     cmp = findKey( token, filterCompare );
810                     if( cmp != (RadFilterComparison)NO_TOKEN ) {
811                     token = strtok( NULL, " " );
812                         if ( token ) {
813                             if ( tok == FILTER_IPX_DST_IPXSOCK ) {
814                                 ipx->dstSocComp = cmp;
815                                 ipx->dstIpxSoc = 
816                             ntohs( (IpxSocket) strtol( token, NULL, 16 ));
817                             } else {
818                                 ipx->srcSocComp = cmp;
819                                 ipx->srcIpxSoc 
820                                     = ntohs( (IpxSocket) strtol( token, NULL, 16 ));
821                             }
822                             break;
823                         }
824                     }
825                 }
826                 goto doneErr;
827              }
828
829             default:
830                 /* no keyword match */
831                 goto doneErr;
832         }
833         token = strtok( NULL, " " ); 
834     } 
835
836     if( elements == IPX_FILTER_COMPLETE ) {
837         return( 0 );
838     }
839
840 doneErr:
841     librad_log("ipx filter error: do not recognize %s in %s \n",
842               token, curString );
843     return( -1 );
844 }
845
846     /*
847      * parseIpFilter:
848      *
849      * This routine parses an IP filter string from a RADIUS
850      * reply. The format of the string is:
851      *
852      *  ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
853      *      [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ] 
854      *
855      * Fields in [...] are optional.
856      *  where:
857      *
858      *  ip:             Keyword to designate an IP filter. Actually this
859      *                  has been determined by parseFilter.
860      *
861      *  dir:            Filter direction. "IN" or "OUT"
862      *
863      *  action:         Filter action. "FORWARD" or "DROP"
864      *
865      *  dstip:          Keyword for destination IP address.
866      *                  n.n.n.n = IP address. /nn - netmask. 
867      *                  
868      *  srcip:          Keyword for source IP address.
869      *                  n.n.n.n = IP address. /nn - netmask. 
870      *                  
871      *  proto:          Optional protocol field. Either a name or
872      *                  number. Known names are in FilterProtoName[].
873      *                  
874      *  dstpost:        Keyword for destination port. Only valid with tcp
875      *                  or udp. 'cmp' are in FilterPortType[]. 'value' can be
876      *                  a name or number.
877      *
878      *  srcpost:        Keyword for source port. Only valid with tcp
879      *                  or udp. 'cmp' are in FilterPortType[]. 'value' can be
880      *                  a name or number.
881      *                  
882      *  est:            Keyword for TCP established. Valid only for tcp.
883      *                  
884      * expects:
885      *
886      *  curEntry:       Pointer to place the filter structure
887      *
888      *  returns:        -1 for error or 0 for OK
889      *  
890      */
891
892 static int 
893 parseIpFilter(curString, curEntry)
894 const char      *curString;
895 RadFilter       *curEntry;
896 {
897  
898     unsigned long       elements = 0l;
899     int                 tok; 
900     char*               token;
901     RadIpFilter*        ip;
902
903     token = strtok( NULL, " " ); 
904
905     memset( curEntry, '\0', sizeof( RadFilter ) );
906     curEntry->type = RAD_FILTER_IP; 
907     ip = &curEntry->u.ip;
908     ip->established = FALSE;
909  
910     while( token ) {
911         tok = findKey( token, filterKeywords );
912         switch( tok ) {
913             case FILTER_IN:
914             case FILTER_OUT:
915                 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
916                 elements |= (1 << FILTER_DIRECTION );
917                 break;
918             case FILTER_FORWARD:
919             case FILTER_DROP:
920                 elements |= (1 << FILTER_DISPOSITION );
921                 if( tok == FILTER_FORWARD ) {
922                     curEntry->forward = TRUE;
923                 } else {
924                     curEntry->forward = FALSE;
925                 }
926                 break;
927             case FILTER_IP_DST:
928             case FILTER_IP_SRC:
929                 token = strtok( NULL, " " );
930                 if ( token ) {
931                     if( tok == FILTER_IP_DST ) {
932                         
933                         if( ipAddressStringToValue( token, 
934                                  &ip->dstip, (char *)&ip->dstmask ) ) {
935                             break;
936                         }
937                     } else {
938                         if( ipAddressStringToValue( token, 
939                                 &ip->srcip, (char *)&ip->srcmask ) ) {
940                             break;
941                         }
942                     }
943                 } 
944
945                 librad_log("ip filter error: do not recognize %s in %s \n",
946                           token, curString );
947                 goto doneErr ;
948
949             case FILTER_IP_DST_PORT:
950             case FILTER_IP_SRC_PORT:
951             {
952                 RadFilterComparison cmp;
953                 short            port;
954
955                 token = strtok( NULL, " " );
956                 if ( token ) {
957                     cmp = findKey( token, filterCompare );
958                     if( cmp != (RadFilterComparison)NO_TOKEN ) {
959                         token = strtok( NULL, " " );
960                         if ( token ) {
961                             if( isAllDigit( token ) ) {
962                                 port = atoi( (char *) token );
963                             } else {
964                                 port = findKey( token, filterPortType );
965                             }
966                             if( port != (short) NO_TOKEN ) {
967                                 if( tok == FILTER_IP_DST_PORT ) {
968                                     ip->dstPortComp = cmp;
969                                     ip->dstport = htons( port );
970                                 } else {
971                                     ip->srcPortComp = cmp;
972                                     ip->srcport = htons( port );
973                                 }
974                                 break;
975                             }
976                         }
977                     }
978                 }
979                 librad_log( "ip filter error: do not recognize %s in %s \n",
980                           token, curString );
981                 goto doneErr;
982                 break;
983             }
984             case FILTER_EST:
985                 ip->established = TRUE;
986                 break;
987             default:
988                 /* no keyword match but may match a protocol list */
989                 if( isAllDigit( token ) ) {
990                     tok = atoi( (char *) token );
991                 } else {
992                     tok = findKey( token, filterProtoName );
993
994                     if( tok == NO_TOKEN ) {
995                         librad_log("ip filter error: do not recognize %s in %s \n",
996                              token, curString );
997                         goto doneErr;
998                     }
999                 }
1000                 ip->proto = tok;
1001         }
1002         token = strtok( NULL, " " ); 
1003     } 
1004
1005     if( elements == IP_FILTER_COMPLETE ) {
1006         return( 0 );
1007     }
1008
1009 doneErr:
1010     return( -1 );
1011 }
1012
1013     /*
1014      * parseGenericFilter:
1015      *
1016      * This routine parses a Generic filter string from a RADIUS
1017      * reply. The format of the string is:
1018      *
1019      *  GENERIC dir action offset mask value [== or != ] [more]
1020      *
1021      * Fields in [...] are optional.
1022      *  where:
1023      *
1024      *  generic:        Keyword to indicate a generic filter. This
1025      *                  has been determined by parseFilter.
1026      *
1027      *  dir:            Filter direction. "IN" or "OUT"
1028      *
1029      *  action:         Filter action. "FORWARD" or "DROP"
1030      *
1031      *  offset:         A Number. Specifies an offset into a frame 
1032      *                  to start comparing.
1033      *                  
1034      *  mask:           A hexadecimal mask of bits to compare.
1035      *                  
1036      *  value:          A value to compare with the masked data.
1037      *
1038      *  compNeq:        Defines type of comparison. ( "==" or "!=")
1039      *                  Default is "==".
1040      *                  
1041      *  more:           Optional keyword MORE, to represent the attachment
1042      *                  to the next entry.
1043      *
1044      * expects:
1045      *
1046      *  curEntry:       Pointer to place the filter structure
1047      *
1048      *  returns:        -1 for error or 0 for OK
1049      *  
1050      */
1051
1052 static int
1053 parseGenericFilter(curString, curEntry)
1054 const char      *curString;
1055 RadFilter       *curEntry;
1056 {
1057     unsigned long       elements = 0l; 
1058     int                 tok; 
1059     int                 gstate = FILTER_GENERIC_OFFSET;
1060     char*               token;
1061     short               valLen, maskLen;
1062     RadGenericFilter*   gen;
1063
1064     token = strtok( NULL, " " ); 
1065
1066     maskLen = 0;
1067     memset( (char *)curEntry, '\0', sizeof( RadFilter ) );
1068     curEntry->type = RAD_FILTER_GENERIC;
1069     gen = &curEntry->u.generic;
1070     gen->more = FALSE; 
1071     gen->compNeq = FALSE;       
1072
1073     while( token ) {
1074         tok = findKey( token, filterKeywords );
1075         switch( tok ) {
1076             case FILTER_IN:
1077             case FILTER_OUT:
1078                 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
1079                 elements |= (1 << FILTER_DIRECTION );
1080                 break;
1081             case FILTER_FORWARD:
1082             case FILTER_DROP:
1083                 elements |= (1 << FILTER_DISPOSITION );
1084                 if( tok == FILTER_FORWARD ) {
1085                     curEntry->forward = TRUE;
1086                 } else {
1087                     curEntry->forward = FALSE;
1088                 }
1089                 break;
1090             case FILTER_GENERIC_COMPNEQ:
1091                 gen->compNeq = TRUE;
1092                 break;
1093             case FILTER_GENERIC_COMPEQ:
1094                 gen->compNeq = FALSE;
1095                 break;
1096             case FILTER_MORE:
1097                 gen->more = htons( TRUE );
1098                 break;
1099             default:
1100                 elements |= ( 1 << gstate );
1101                 switch( gstate ) {
1102                     case FILTER_GENERIC_OFFSET:
1103                         gstate = FILTER_GENERIC_MASK;
1104                         gen->offset = htons( atoi( (char *) token ) );
1105                         break;
1106                     case FILTER_GENERIC_MASK:
1107                         gstate = FILTER_GENERIC_VALUE;
1108                         maskLen = a2octet( token, (char *)gen->mask );
1109                         if( maskLen == (short) -1 ) {
1110                             librad_log("filter mask error: %s \n", curString );
1111                             goto doneErr;
1112                         }
1113                         break;
1114                     case FILTER_GENERIC_VALUE:
1115                         gstate ++;
1116                         valLen = a2octet( token, (char *)gen->value );
1117                         if( valLen != maskLen ) {
1118                             librad_log("filter value size is not the same size as the filter mask: %s \n", 
1119                                      curString );
1120                             goto doneErr;
1121                         }
1122                         gen->len = htons( valLen );
1123                         break;
1124                     default:
1125                         librad_log("filter: do not know %s in %s \n",
1126                                  token, curString );
1127                         goto doneErr;    
1128                 }
1129         }
1130         token = strtok( NULL, " " ); 
1131     }
1132
1133     if( elements == GENERIC_FILTER_COMPLETE ) {
1134         return( 0 );
1135     }
1136
1137 doneErr:
1138     return( -1 );
1139 }
1140                        
1141     /*
1142      * filterBinary:
1143      *
1144      * This routine will call routines to parse entries from an ASCII format
1145      * to a binary format recognized by the Ascend boxes.
1146      *
1147      *  pair:                   Pointer to value_pair to place return.
1148      *
1149      *  valstr:                 The string to parse     
1150      *
1151      *  return:                 -1 for error or 0.
1152      */
1153 int 
1154 filterBinary(VALUE_PAIR *pair, const char *valstr)
1155 {
1156
1157     char*               token;
1158     unsigned long       tok;
1159     int                 rc;
1160     RadFilter           radFil, *filt;
1161     RadGenericFilter*   gen;
1162     static VALUE_PAIR   *prevRadPair = NULL;
1163     char                *copied_valstr;
1164
1165     rc = -1;
1166
1167     /*
1168      *  Copy the valstr, so we don't smash it in place via strtok!
1169      */
1170     copied_valstr = strdup(valstr);
1171     if (!copied_valstr) return -1;
1172
1173     token = strtok(copied_valstr, " " );
1174     tok = findKey( token, filterType );
1175     pair->length = SIZEOF_RADFILTER;
1176     switch( tok ) {
1177       case RAD_FILTER_GENERIC:
1178         rc = parseGenericFilter( valstr, &radFil );
1179         break;
1180       case RAD_FILTER_IP:
1181         rc = parseIpFilter( valstr, &radFil );
1182         break;
1183       case RAD_FILTER_IPX:
1184         rc = parseIpxFilter( valstr, &radFil );
1185         break;
1186       default:
1187         librad_log("filterBinary: unknown filter type \"%s\"", token);
1188         free(copied_valstr);
1189         return -1;
1190         break;
1191     }
1192     free(copied_valstr);
1193     copied_valstr = NULL;
1194
1195     /*
1196      * if 'more' is set then this new entry must exist, be a 
1197      * FILTER_GENERIC_TYPE, direction and disposition must match for 
1198      * the previous 'more' to be valid. If any should fail then TURN OFF 
1199      * previous 'more'
1200      */
1201     if( prevRadPair ) {
1202         filt = ( RadFilter * )prevRadPair->strvalue;
1203         if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
1204            ( prevRadPair->attribute != pair->attribute ) || 
1205            ( filt->indirection != radFil.indirection ) || 
1206            ( filt->forward != radFil.forward ) ) {
1207             gen = &filt->u.generic;
1208             gen->more = FALSE;
1209             librad_log("filterBinary:  'more' for previous entry doesn't match: %s.\n",
1210                      valstr);
1211         }
1212     }
1213     prevRadPair = NULL;
1214     if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
1215         if( radFil.u.generic.more ) {
1216             prevRadPair = pair;
1217         } 
1218     }
1219
1220     if( rc != -1 ) {
1221         memcpy( pair->strvalue, (char *) &radFil, pair->length );
1222     }
1223     return(rc);
1224 }
1225
1226
1227 /********************************************************************/
1228
1229 /*
1230  *  The following code was written specifically for the FreeRADIUS
1231  *  server by Alan DeKok <aland@ox.org>, and as such, falls under
1232  *  the GPL, and not under the previous Ascend license.
1233  */
1234
1235 static const char *FindValue(unsigned int value, const KeywordStruct *list)
1236 {
1237   const KeywordStruct   *entry;
1238
1239   entry = list;
1240   while (entry->name) {
1241     if (entry->value == value) {
1242       return entry->name;
1243     }
1244     entry++;
1245   }
1246
1247   return "???";
1248 }
1249
1250 /*
1251  *      Print an Ascend binary filter attribute to a string,
1252  *      Grrr... Ascend makes the server do this work, instead
1253  *      of doing it on the NAS.
1254  *
1255  *      Note we don't bother checking 'len' after the snprintf's.
1256  *      This function should ONLY be called with a large (~1k) buffer.
1257  */
1258 void print_abinary(VALUE_PAIR *vp, u_char *buffer, int len)
1259 {
1260   int i;
1261   char *p;
1262   RadFilter     filter;
1263   
1264   static const char *action[] = {"drop", "forward"};
1265   static const char *direction[] = {"output", "input"};
1266   
1267   p = (char *)buffer;
1268
1269   /*
1270    *  Just for paranoia: wrong size filters get printed as octets
1271    */
1272   if (vp->length > SIZEOF_RADFILTER) {
1273     strcpy(p, "0x");
1274     p += 2;
1275     for (i = 0; i < vp->length; i++) {
1276       sprintf(p, " %02x", vp->strvalue[i]);
1277       p += 3;
1278     }
1279     return;
1280   }
1281
1282   memcpy(&filter, vp->strvalue, SIZEOF_RADFILTER); /* alignment issues */
1283   *(p++) = '"';
1284   len -= 3;                     /* account for leading & trailing quotes */
1285
1286   i = snprintf(p, len, "%s %s %s",
1287                FindValue(filter.type, filterType),
1288                direction[filter.indirection & 0x01],
1289                action[filter.forward & 0x01]);
1290
1291   p += i;
1292   len -= i;
1293
1294   /*
1295    *    Handle IP filters
1296    */
1297   if (filter.type == RAD_FILTER_IP) {
1298
1299     i =  snprintf(p, len, " %s", 
1300                   FindValue(filter.u.ip.proto, filterProtoName));
1301     p += i;
1302     len -= i;
1303     
1304     if (filter.u.ip.dstip) {
1305       i = snprintf(p, len, " dstip %d.%d.%d.%d/%d",
1306                    ((u_char *) &filter.u.ip.dstip)[0],
1307                    ((u_char *) &filter.u.ip.dstip)[1],
1308                    ((u_char *) &filter.u.ip.dstip)[2],
1309                    ((u_char *) &filter.u.ip.dstip)[3],
1310                    filter.u.ip.dstmask);
1311       p += i;
1312       len -= i;
1313     }
1314     
1315     if (filter.u.ip.srcip) {
1316       i = snprintf(p, len, " srcip %d.%d.%d.%d/%d",
1317                    ((u_char *) &filter.u.ip.srcip)[0],
1318                    ((u_char *) &filter.u.ip.srcip)[1],
1319                    ((u_char *) &filter.u.ip.srcip)[2],
1320                    ((u_char *) &filter.u.ip.srcip)[3],
1321                    filter.u.ip.srcmask);
1322       p += i;
1323       len -= i;
1324     }
1325
1326     if (filter.u.ip.dstPortComp) {
1327       i = snprintf(p, len, " dstport %s %d",
1328                    FindValue(filter.u.ip.dstPortComp, filterCompare),
1329                    ntohs(filter.u.ip.dstport));
1330       p += i;
1331       len -= i;
1332     }
1333     
1334     if (filter.u.ip.srcPortComp) {
1335       i = snprintf(p, len, " srcport %s %d",
1336                    FindValue(filter.u.ip.srcPortComp, filterCompare),
1337                    ntohs(filter.u.ip.srcport));
1338       p += i;
1339       len -= i;
1340     }
1341
1342     if (filter.u.ip.established) {
1343       i = snprintf(p, len, " est");
1344       p += i;
1345       len -= i;
1346     }
1347
1348     /*
1349      *  Handle IPX filters
1350      */
1351   } else if (filter.type == RAD_FILTER_IPX) {
1352     /* print for source */
1353     if (filter.u.ipx.srcIpxNet) {
1354       i = snprintf(p, len, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
1355                   (unsigned int)ntohl(filter.u.ipx.srcIpxNet),
1356                   filter.u.ipx.srcIpxNode[0], filter.u.ipx.srcIpxNode[1], 
1357                   filter.u.ipx.srcIpxNode[2], filter.u.ipx.srcIpxNode[3], 
1358                   filter.u.ipx.srcIpxNode[4], filter.u.ipx.srcIpxNode[5]);
1359       p += i;
1360       len -= i;
1361
1362       if (filter.u.ipx.srcSocComp) {
1363         i = snprintf(p, len, " srcipxsock %s 0x%04x",
1364                      FindValue(filter.u.ipx.srcSocComp, filterCompare),
1365                      ntohs(filter.u.ipx.srcIpxSoc));
1366         p += i;
1367         len -= i;
1368       }
1369     }
1370
1371     /* same for destination */
1372     if (filter.u.ipx.dstIpxNet) {
1373       i = snprintf(p, len, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
1374                   (unsigned int)ntohl(filter.u.ipx.dstIpxNet),
1375                   filter.u.ipx.dstIpxNode[0], filter.u.ipx.dstIpxNode[1], 
1376                   filter.u.ipx.dstIpxNode[2], filter.u.ipx.dstIpxNode[3], 
1377                   filter.u.ipx.dstIpxNode[4], filter.u.ipx.dstIpxNode[5]);
1378       p += i;
1379       len -= i;
1380
1381       if (filter.u.ipx.dstSocComp) {
1382         i = snprintf(p, len, " dstipxsock %s 0x%04x",
1383                      FindValue(filter.u.ipx.dstSocComp, filterCompare),
1384                      ntohs(filter.u.ipx.dstIpxSoc));
1385         p += i;
1386         len -= i;
1387       }
1388     }
1389
1390
1391   } else if (filter.type == RAD_FILTER_GENERIC) {
1392     int count;
1393
1394     i = snprintf(p, len, " %d ", filter.u.generic.offset);
1395     p += i;
1396     i -= len;
1397
1398     /* show the mask */
1399     for (count = 0; count < ntohs(filter.u.generic.len); count++) {
1400       i = snprintf(p, len, "%02x", filter.u.generic.mask[count]);
1401       p += i;
1402       len -= i;
1403     }
1404
1405     strcpy(p, " ");
1406     p++;
1407     len--;
1408
1409     /* show the value */
1410     for (count = 0; count < ntohs(filter.u.generic.len); count++) {
1411       i = snprintf(p, len, "%02x", filter.u.generic.value[count]);
1412       p += i;
1413       len -= i;
1414     }
1415
1416     i = snprintf(p, len, " %s", (filter.u.generic.compNeq) ? "!=" : "==");
1417     p += i;
1418     len -= i;
1419   }
1420
1421   *(p++) = '"';
1422   *p = '\0';
1423 }