All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
filters.c
Go to the documentation of this file.
1 /*
2  * filters.c Routines to parse Ascend's filter attributes.
3  *
4  * Version: $Id: e9e7bf0956e55b627c4b32300f8dd3640b6f2120 $
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2003,2006 The FreeRADIUS server project
21  */
22 
23 RCSID("$Id: e9e7bf0956e55b627c4b32300f8dd3640b6f2120 $")
24 
25 #include <freeradius-devel/libradius.h>
26 
27 #ifdef WITH_ASCEND_BINARY
28 #include <ctype.h>
29 
30 /*
31  * Two types of filters are supported, GENERIC and IP. The identifiers
32  * are:
33  */
34 
35 #define RAD_FILTER_GENERIC 0
36 #define RAD_FILTER_IP 1
37 #define RAD_FILTER_IPX 2
38 
39 /*
40  * Generic filters mask and match up to RAD_MAX_FILTER_LEN bytes
41  * starting at some offset. The length is:
42  */
43 #define RAD_MAX_FILTER_LEN 6
44 
45 /*
46  * ASCEND extensions for ABINARY filters
47  */
48 
49 #define IPX_NODE_ADDR_LEN 6
50 
51 #if ! defined( false )
52 # define false 0
53 # define true (! false)
54 #endif
55 
56 
57 /*
58  * ascend_ip_filter_t
59  *
60  * The binary format of an IP filter. ALL fields are stored in
61  * network byte order.
62  *
63  * srcip: The source IP address.
64  *
65  * dstip: The destination IP address.
66  *
67  * srcmask: The number of leading one bits in the source address
68  * mask. Specifies the bits of interest.
69  *
70  * dstmask: The number of leading one bits in the destination
71  * address mask. Specifies the bits of interest.
72  *
73  * proto: The IP protocol number
74  *
75  * established: A boolean value. true when we care about the
76  * established state of a TCP connection. false when
77  * we dont care.
78  *
79  * srcport: TCP or UDP source port number.
80  *
81  * dstport: TCP or UDP destination port number.
82  *
83  * srcPortCmp: One of the values of the RadFilterComparison
84  * enumeration, specifying how to compare the
85  * srcport value.
86  *
87  * dstPortCmp: One of the values of the RadFilterComparison
88  * enumeration, specifying how to compare the
89  * dstport value.
90  *
91  * fill: Round things out to a int16_t boundary.
92  */
93 typedef struct ascend_ip_filter_t {
94  uint32_t srcip;
95  uint32_t dstip;
96  uint8_t srcmask;
97  uint8_t dstmask;
98  uint8_t proto;
99  uint8_t established;
100  uint16_t srcport;
101  uint16_t dstport;
102  uint8_t srcPortComp;
103  uint8_t dstPortComp;
104  unsigned char fill[4]; /* used to be fill[2] */
105 } ascend_ip_filter_t;
106 
107 
108 /*
109  * ascend_ipx_net_t
110  *
111  * net: IPX Net address
112  *
113  * node: IPX Node address
114  *
115  * socket: IPX socket address
116  */
117 typedef struct ascend_ipx_net_t {
118  uint32_t net;
119  uint8_t node[IPX_NODE_ADDR_LEN];
120  uint16_t socket;
121 } ascend_ipx_net_t;
122 
123 /*
124  * ascend_ipx_filter_t
125  *
126  * The binary format of an IPX filter. ALL fields are stored in
127  * network byte order.
128  *
129  * src: Source net, node, and socket.
130  *
131  * dst: Destination net, node, and socket.
132  *
133  * srcSocComp: Source socket compare value
134  *
135  * dstSocComp: Destination socket compare value
136  */
137 typedef struct ascend_ipx_filter_t {
138  ascend_ipx_net_t src;
139  ascend_ipx_net_t dst;
140  uint8_t srcSocComp;
141  uint8_t dstSocComp;
142 } ascend_ipx_filter_t;
143 
144 
145 /*
146  * ascend_generic_filter_t
147  *
148  * The binary format of a GENERIC filter. ALL fields are stored in
149  * network byte order.
150  *
151  * offset: Number of bytes into packet to start comparison.
152  *
153  * len: Number of bytes to mask and compare. May not
154  * exceed RAD_MAX_FILTER_LEN.
155  *
156  * more: Boolean. If non-zero the next filter entry is
157  * also to be applied to a packet.
158  *
159  * mask: A bit mask specifying the bits to compare.
160  *
161  * value: A value to compare against the masked bits at
162  * offset in a users packet.
163  *
164  * compNeq: Defines type of comarison (Equal or Notequal)
165  * default is Equal.
166  *
167  * fill: Round things out to a dword boundary
168  */
169 typedef struct ascend_generic_filter_t {
170  uint16_t offset;
171  uint16_t len;
172  uint16_t more;
173  uint8_t mask[ RAD_MAX_FILTER_LEN ];
174  uint8_t value[ RAD_MAX_FILTER_LEN ];
175  uint8_t compNeq;
176  uint8_t fill[3]; /* used to be fill[1] */
177 } ascend_generic_filter_t;
178 
179 /*
180  * ascend_filter_t
181  *
182  * A binary filter element. Contains one of ascend_ip_filter_t,
183  * ascend_ipx_filter_t, or ascend_generic_filter_t.
184  *
185  * All fields are stored in network byte order.
186  *
187  * type: Either RAD_FILTER_GENERIC or RAD_FILTER_IP.
188  *
189  * forward: true if we should forward packets that match this
190  * filter, false if we should drop packets that match
191  * this filter.
192  *
193  * direction: true if this is an input filter, false if this is
194  * an output filter.
195  *
196  * fill: Round things out to a dword boundary.
197  *
198  * u: A union of
199  * ip: An ip filter entry
200  * generic: A generic filter entry
201  */
202 typedef struct ascend_filter_t {
203  uint8_t type;
204  uint8_t forward;
205  uint8_t direction;
206  uint8_t fill;
207  union {
208  ascend_ip_filter_t ip;
209  ascend_ipx_filter_t ipx;
210  ascend_generic_filter_t generic;
211  uint8_t data[28]; /* ensure it's 32 bytes */
212  } u;
213 } ascend_filter_t;
214 
215 /*
216  * This is a wild C hack...
217  */
218 typedef struct _cpp_hack {
219  char data[(sizeof(ascend_filter_t) == 32) ? 1 : -1 ];
220 } _cpp_hack;
221 
222 /*
223  * FilterPortType:
224  *
225  * Ascii names of some well known tcp/udp services.
226  * Used for filtering on a port type.
227  *
228  * ??? What the heck is wrong with getservbyname?
229  */
230 static const FR_NAME_NUMBER filterPortType[] = {
231  { "ftp-data", 20 },
232  { "ftp", 21 },
233  { "telnet", 23 },
234  { "smtp", 25 },
235  { "nameserver", 42 },
236  { "domain", 53 },
237  { "tftp", 69 },
238  { "gopher", 70 },
239  { "finger", 79 },
240  { "www", 80 },
241  { "kerberos", 88 },
242  { "hostname", 101 },
243  { "nntp", 119 },
244  { "ntp", 123 },
245  { "exec", 512 },
246  { "login", 513 },
247  { "cmd", 514 },
248  { "talk", 517 },
249  { NULL , 0},
250 };
251 
252 static const FR_NAME_NUMBER filterType[] = {
253  { "generic", RAD_FILTER_GENERIC},
254  { "ip", RAD_FILTER_IP},
255  { "ipx", RAD_FILTER_IPX},
256  { NULL, 0},
257 };
258 
259 typedef enum {
260  FILTER_GENERIC_TYPE,
261  FILTER_IP_TYPE,
262  FILTER_IN,
263  FILTER_OUT,
264  FILTER_FORWARD,
265  FILTER_DROP,
266  FILTER_GENERIC_OFFSET,
267  FILTER_GENERIC_MASK,
268  FILTER_GENERIC_VALUE,
269  FILTER_GENERIC_COMPNEQ,
270  FILTER_GENERIC_COMPEQ,
271  FILTER_MORE,
272  FILTER_IP_DST,
273  FILTER_IP_SRC,
274  FILTER_IP_PROTO,
275  FILTER_IP_DST_PORT,
276  FILTER_IP_SRC_PORT,
277  FILTER_EST,
278  FILTER_IPX_TYPE,
279  FILTER_IPX_DST_IPXNET,
280  FILTER_IPX_DST_IPXNODE,
281  FILTER_IPX_DST_IPXSOCK,
282  FILTER_IPX_SRC_IPXNET,
283  FILTER_IPX_SRC_IPXNODE,
284  FILTER_IPX_SRC_IPXSOCK
285 } FilterTokens;
286 
287 
288 static const FR_NAME_NUMBER filterKeywords[] = {
289  { "ip", FILTER_IP_TYPE },
290  { "generic", FILTER_GENERIC_TYPE },
291  { "in", FILTER_IN },
292  { "out", FILTER_OUT },
293  { "forward", FILTER_FORWARD },
294  { "drop", FILTER_DROP },
295  { "dstip", FILTER_IP_DST },
296  { "srcip", FILTER_IP_SRC },
297  { "dstport", FILTER_IP_DST_PORT },
298  { "srcport", FILTER_IP_SRC_PORT },
299  { "est", FILTER_EST },
300  { "more", FILTER_MORE },
301  { "!=", FILTER_GENERIC_COMPNEQ },
302  { "==", FILTER_GENERIC_COMPEQ },
303  { "ipx", FILTER_IPX_TYPE },
304  { "dstipxnet", FILTER_IPX_DST_IPXNET },
305  { "dstipxnode", FILTER_IPX_DST_IPXNODE },
306  { "dstipxsock", FILTER_IPX_DST_IPXSOCK },
307  { "srcipxnet", FILTER_IPX_SRC_IPXNET },
308  { "srcipxnode", FILTER_IPX_SRC_IPXNODE },
309  { "srcipxsock", FILTER_IPX_SRC_IPXSOCK },
310  { NULL , -1},
311 };
312 
313 /*
314  * FilterProtoName:
315  *
316  * Ascii name of protocols used for filtering.
317  *
318  * ??? What the heck is wrong with getprotobyname?
319  */
320 static const FR_NAME_NUMBER filterProtoName[] = {
321  { "tcp", 6 },
322  { "udp", 17 },
323  { "ospf", 89 },
324  { "icmp", 1 },
325  { "0", 0 },
326  { NULL , -1 },
327 };
328 
329 
330 /*
331  * RadFilterComparison:
332  *
333  * An enumerated values for the IP filter port comparisons.
334  */
335 typedef enum {
336  RAD_NO_COMPARE = 0,
337  RAD_COMPARE_LESS,
338  RAD_COMPARE_EQUAL,
339  RAD_COMPARE_GREATER,
340  RAD_COMPARE_NOT_EQUAL
341 } RadFilterComparison;
342 
343 static const FR_NAME_NUMBER filterCompare[] = {
344  { "<", RAD_COMPARE_LESS },
345  { "=", RAD_COMPARE_EQUAL },
346  { ">", RAD_COMPARE_GREATER },
347  { "!=", RAD_COMPARE_NOT_EQUAL },
348  { NULL, 0 },
349 };
350 
351 
352 /*
353  * ascend_parse_ipx_net
354  *
355  * srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]
356  */
357 static int ascend_parse_ipx_net(int argc, char **argv,
358  ascend_ipx_net_t *net, uint8_t *comp)
359 {
360  int token;
361  char const *p;
362 
363  if (argc < 3) return -1;
364 
365  /*
366  * Parse the net, which is a hex number.
367  */
368  net->net = htonl(strtol(argv[0], NULL, 16));
369 
370  /*
371  * Parse the node.
372  */
373  token = fr_str2int(filterKeywords, argv[1], -1);
374  switch (token) {
375  case FILTER_IPX_SRC_IPXNODE:
376  case FILTER_IPX_DST_IPXNODE:
377  break;
378 
379  default:
380  return -1;
381  }
382 
383  /*
384  * Can have a leading "0x" or "0X"
385  */
386  p = argv[2];
387  if ((memcmp(p, "0X", 2) == 0) ||
388  (memcmp(p, "0x", 2) == 0)) p += 2;
389 
390  /*
391  * Node must be 6 octets long.
392  */
393  token = fr_hex2bin(net->node, IPX_NODE_ADDR_LEN, p, strlen(p));
394  if (token != IPX_NODE_ADDR_LEN) return -1;
395 
396  /*
397  * Nothing more, die.
398  */
399  if (argc == 3) return 3;
400 
401  /*
402  * Can't be too little or too much.
403  */
404  if (argc != 6) return -1;
405 
406  /*
407  * Parse the socket.
408  */
409  token = fr_str2int(filterKeywords, argv[3], -1);
410  switch (token) {
411  case FILTER_IPX_SRC_IPXSOCK:
412  case FILTER_IPX_DST_IPXSOCK:
413  break;
414 
415  default:
416  return -1;
417  }
418 
419  /*
420  * Parse the command "<", ">", "=" or "!="
421  */
422  token = fr_str2int(filterCompare, argv[4], -1);
423  switch (token) {
424  case RAD_COMPARE_LESS:
425  case RAD_COMPARE_EQUAL:
426  case RAD_COMPARE_GREATER:
427  case RAD_COMPARE_NOT_EQUAL:
428  *comp = token;
429  break;
430 
431  default:
432  return -1;
433  }
434 
435  /*
436  * Parse the value.
437  */
438  token = strtoul(argv[5], NULL, 16);
439  if (token > 65535) return -1;
440 
441  net->socket = token;
442  net->socket = htons(net->socket);
443 
444 
445  /*
446  * Everything's OK, we parsed 6 entries.
447  */
448  return 6;
449 }
450 
451 /*
452  * ascend_parse_ipx_filter
453  *
454  * This routine parses an IPX filter string from a string.
455  * The format of the string is:
456  *
457  * [ srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]]
458  * [ dstipxnet nnnn dstipxnode mmmmm [dstipxsoc cmd value ]]
459  *
460  * Fields in [...] are optional.
461  * where:
462  *
463  * srcipxnet: Keyword for source IPX address.
464  * nnnn = IPX Node address.
465  *
466  * srcipxnode: Keyword for source IPX Node address.
467  * mmmmm = IPX Node Address, could be FFFFFF.
468  * A vlid ipx node number should accompany ipx net number.
469  *
470  * srcipxsoc: Keyword for source IPX socket address.
471  *
472  * cmd: One of ">" or "<" or "=" or "!=".
473  *
474  * value: Socket value to be compared against, in hex.
475  *
476  * dstipxnet: Keyword for destination IPX address.
477  * nnnn = IPX Node address.
478  *
479  * dstipxnode: Keyword for destination IPX Node address.
480  * mmmmm = IPX Node Address, could be FFFFFF.
481  * A valid ipx node number should accompany ipx net number.
482  *
483  * dstipxsoc: Keyword for destination IPX socket address.
484  *
485  * cmd: One of ">" or "<" or "=" or "!=".
486  *
487  * value: Socket value to be compared against, in hex.
488  */
489 static int ascend_parse_ipx(int argc, char **argv, ascend_ipx_filter_t *filter)
490 {
491  int rcode;
492  int token;
493  int flags = 0;
494 
495  /*
496  * We may have nothing, in which case we simply return.
497  */
498  if (argc == 0) return 0;
499 
500  /*
501  * Must have "net N node M"
502  */
503  if (argc < 4) return -1;
504 
505  while ((argc > 0) && (flags != 0x03)) {
506  token = fr_str2int(filterKeywords, argv[0], -1);
507  switch (token) {
508  case FILTER_IPX_SRC_IPXNET:
509  if (flags & 0x01) return -1;
510  rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
511  &(filter->src),
512  &(filter->srcSocComp));
513  if (rcode < 0) return -1;
514  argc -= (rcode + 1);
515  argv += rcode + 1;
516  flags |= 0x01;
517  break;
518 
519  case FILTER_IPX_DST_IPXNET:
520  if (flags & 0x02) return -1;
521  rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
522  &(filter->dst),
523  &(filter->dstSocComp));
524  if (rcode < 0) return -1;
525  argc -= (rcode + 1);
526  argv += rcode + 1;
527  flags |= 0x02;
528  break;
529 
530  default:
531  fr_strerror_printf("Unknown string \"%s\" in IPX data filter",
532  argv[0]);
533  return -1;
534  }
535  }
536 
537  /*
538  * Arguments left over: die.
539  */
540  if (argc != 0) return -1;
541 
542  /*
543  * Everything's OK.
544  */
545  return 0;
546 }
547 
548 
549 /*
550  * Parse an IP address and optionally a netmask, to a uint32_t.
551  *
552  * ipaddr should already be initialized to zero.
553  * ipaddr is in network byte order.
554  *
555  * Returns -1 on failure, or the number of bits in the netmask, otherwise.
556  */
557 static int ascend_parse_ipaddr(uint32_t *ipaddr, char *str)
558 {
559  int count = 0;
560  int ip[4];
561  int masklen;
562  uint32_t netmask = 0;
563 
564  /*
565  * Look for IP's.
566  */
567  count = 0;
568  while (*str && (count < 4) && (netmask == 0)) {
569  next:
570  ip[count] = 0;
571 
572  while (*str) {
573  switch (*str) {
574  case '0': case '1': case '2': case '3':
575  case '4': case '5': case '6': case '7':
576  case '8': case '9':
577  ip[count] *= 10;
578  ip[count] += (*str) - '0';
579  str++;
580  break;
581 
582 
583  case '.': /* dot between IP numbers. */
584  str++;
585  if (ip[count] > 255) return -1;
586 
587  /*
588  * 24, 16, 8, 0, done.
589  */
590  *ipaddr |= (ip[count] << (8 * (3 - count)));
591  count++;
592  goto next;
593 
594  case '/': /* netmask */
595  str++;
596  masklen = atoi(str);
597  if ((masklen < 0) || (masklen > 32)) return -1;
598  str += strspn(str, "0123456789");
599  netmask = masklen;
600  goto finalize;
601 
602  default:
603  fr_strerror_printf("Invalid character in IP address");
604  return -1;
605  }
606  } /* loop over one character */
607  } /* loop until the count hits 4 */
608 
609  if (count == 3) {
610  finalize:
611  /*
612  * Do the last one, too.
613  */
614  if (ip[count] > 255) return -1;
615 
616  /*
617  * 24, 16, 8, 0, done.
618  */
619  *ipaddr |= (ip[count] << (8 * (3 - count)));
620  }
621 
622  /*
623  * We've hit the end of the IP address, and there's something
624  * else left over: die.
625  */
626  if (*str) return -1;
627 
628  /*
629  * Set the default netmask.
630  */
631  if (!netmask) {
632  if (!*ipaddr) {
633  netmask = 0;
634  } else if ((*ipaddr & 0x80000000) == 0) {
635  netmask = 8;
636  } else if ((*ipaddr & 0xc0000000) == 0x80000000) {
637  netmask = 16;
638  } else if ((*ipaddr & 0xe0000000) == 0xc0000000) {
639  netmask = 24;
640  } else {
641  netmask = 32;
642  }
643  }
644 
645  *ipaddr = htonl(*ipaddr);
646  return netmask;
647 }
648 
649 /*
650  * ascend_parse_port: Parse a comparator and port.
651  *
652  * Returns -1 on failure, or the comparator.
653  */
654 static int ascend_parse_port(uint16_t *port, char *compare, char *str)
655 {
656  int rcode, token = -1;
657 
658  /*
659  * There MUST be a comparison string.
660  */
661  rcode = fr_str2int(filterCompare, compare, -1);
662  if (rcode < 0) return rcode;
663 
664  if (strspn(str, "0123456789") == strlen(str)) {
665  token = atoi(str);
666  } else {
667  token = fr_str2int(filterPortType, str, -1);
668  }
669 
670  if ((token < 0) || (token > 65535)) return -1;
671 
672  *port = token;
673  *port = htons(*port);
674 
675  return rcode;
676 }
677 
678 
679 #define IP_SRC_ADDR_FLAG (1 << 0)
680 #define IP_DEST_ADDR_FLAG (1 << 1)
681 #define IP_SRC_PORT_FLAG (1 << 2)
682 #define IP_DEST_PORT_FLAG (1 << 3)
683 #define IP_PROTO_FLAG (1 << 4)
684 #define IP_EST_FLAG (1 << 5)
685 
686 #define DONE_FLAGS (IP_SRC_ADDR_FLAG | IP_DEST_ADDR_FLAG | \
687  IP_SRC_PORT_FLAG | IP_DEST_PORT_FLAG | \
688  IP_PROTO_FLAG | IP_EST_FLAG)
689 
690 /*
691  * ascend_parse_ip:
692  *
693  * This routine parses an IP filter string from a RADIUS
694  * reply. The format of the string is:
695  *
696  * ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
697  * [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ]
698  *
699  * Fields in [...] are optional.
700  *
701  * dstip: Keyword for destination IP address.
702  * n.n.n.n = IP address. /nn - netmask.
703  *
704  * srcip: Keyword for source IP address.
705  * n.n.n.n = IP address. /nn - netmask.
706  *
707  * proto: Optional protocol field. Either a name or
708  * number. Known names are in FilterProtoName[].
709  *
710  * dstport: Keyword for destination port. Only valid with tcp
711  * or udp. 'cmp' are in FilterPortType[]. 'value' can be
712  * a name or number.
713  *
714  * srcport: Keyword for source port. Only valid with tcp
715  * or udp. 'cmp' are in FilterPortType[]. 'value' can be
716  * a name or number.
717  *
718  * est: Keyword for TCP established. Valid only for tcp.
719  *
720  */
721 static int ascend_parse_ip(int argc, char **argv, ascend_ip_filter_t *filter)
722 {
723  int rcode;
724  int token;
725  int flags;
726 
727  /*
728  * We may have nothing, in which case we simply return.
729  */
730  if (argc == 0) return 0;
731 
732  /*
733  * There may, or may not, be src & dst IP's in the string.
734  */
735  flags = 0;
736  while ((argc > 0) && (flags != DONE_FLAGS)) {
737  token = fr_str2int(filterKeywords, argv[0], -1);
738  switch (token) {
739  case FILTER_IP_SRC:
740  if (flags & IP_SRC_ADDR_FLAG) return -1;
741  if (argc < 2) return -1;
742 
743  rcode = ascend_parse_ipaddr(&filter->srcip, argv[1]);
744  if (rcode < 0) return rcode;
745 
746  filter->srcmask = rcode;
747  flags |= IP_SRC_ADDR_FLAG;
748  argv += 2;
749  argc -= 2;
750  break;
751 
752  case FILTER_IP_DST:
753  if (flags & IP_DEST_ADDR_FLAG) return -1;
754  if (argc < 2) return -1;
755 
756  rcode = ascend_parse_ipaddr(&filter->dstip, argv[1]);
757  if (rcode < 0) return rcode;
758 
759  filter->dstmask = rcode;
760  flags |= IP_DEST_ADDR_FLAG;
761  argv += 2;
762  argc -= 2;
763  break;
764 
765  case FILTER_IP_SRC_PORT:
766  if (flags & IP_SRC_PORT_FLAG) return -1;
767  if (argc < 3) return -1;
768 
769  rcode = ascend_parse_port(&filter->srcport,
770  argv[1], argv[2]);
771  if (rcode < 0) return rcode;
772  filter->srcPortComp = rcode;
773 
774  flags |= IP_SRC_PORT_FLAG;
775  argv += 3;
776  argc -= 3;
777  break;
778 
779  case FILTER_IP_DST_PORT:
780  if (flags & IP_DEST_PORT_FLAG) return -1;
781  if (argc < 3) return -1;
782 
783  rcode = ascend_parse_port(&filter->dstport,
784  argv[1], argv[2]);
785  if (rcode < 0) return rcode;
786  filter->dstPortComp = rcode;
787 
788  flags |= IP_DEST_PORT_FLAG;
789  argv += 3;
790  argc -= 3;
791  break;
792 
793  case FILTER_EST:
794  if (flags & IP_EST_FLAG) return -1;
795  filter->established = 1;
796  argv++;
797  argc--;
798  flags |= IP_EST_FLAG;
799  break;
800 
801  default:
802  if (flags & IP_PROTO_FLAG) return -1;
803  if (strspn(argv[0], "0123456789") == strlen(argv[0])) {
804  token = atoi(argv[0]);
805  } else {
806  token = fr_str2int(filterProtoName, argv[0], -1);
807  if (token == -1) {
808  fr_strerror_printf("Unknown IP protocol \"%s\" in IP data filter",
809  argv[0]);
810  return -1;
811  }
812  }
813  filter->proto = token;
814  flags |= IP_PROTO_FLAG;
815 
816  argv++;
817  argc--;
818  break;
819  }
820  }
821 
822  /*
823  * We should have parsed everything by now.
824  */
825  if (argc != 0) {
826  fr_strerror_printf("Unknown extra string \"%s\" in IP data filter",
827  argv[0]);
828  return -1;
829  }
830 
831  return 0;
832 }
833 
834 
835 /*
836  * ascend_parse_generic
837  *
838  * This routine parses a Generic filter string from a RADIUS
839  * reply. The format of the string is:
840  *
841  * generic dir action offset mask value [== or != ] [more]
842  *
843  * Fields in [...] are optional.
844  *
845  * offset: A Number. Specifies an offset into a frame
846  * to start comparing.
847  *
848  * mask: A hexadecimal mask of bits to compare.
849  *
850  * value: A value to compare with the masked data.
851  *
852  * compNeq: Defines type of comparison. ( "==" or "!=")
853  * Default is "==".
854  *
855  * more: Optional keyword MORE, to represent the attachment
856  * to the next entry.
857  */
858 static int ascend_parse_generic(int argc, char **argv,
859  ascend_generic_filter_t *filter)
860 {
861  int rcode;
862  int token;
863  int flags;
864 
865  /*
866  * We may have nothing, in which case we simply return.
867  */
868  if (argc == 0) return 0;
869 
870  /*
871  * We need at least "offset mask value"
872  */
873  if (argc < 3) return -1;
874 
875  /*
876  * No more than optional comparison and "more"
877  */
878  if (argc > 5) return -1;
879 
880  /*
881  * Offset is a uint16_t number.
882  */
883  if (strspn(argv[0], "0123456789") != strlen(argv[0])) return -1;
884 
885  rcode = atoi(argv[0]);
886  if (rcode > 65535) return -1;
887 
888  filter->offset = rcode;
889  filter->offset = htons(filter->offset);
890 
891  rcode = fr_hex2bin(filter->mask, sizeof(filter->mask), argv[1], strlen(argv[1]));
892  if (rcode != sizeof(filter->mask)) return -1;
893 
894  token = fr_hex2bin(filter->value, sizeof(filter->value), argv[2], strlen(argv[2]));
895  if (token != sizeof(filter->value)) return -1;
896 
897  filter->len = rcode;
898  filter->len = htons(filter->len);
899 
900  /*
901  * Nothing more. Exit.
902  */
903  if (argc == 3) return 0;
904 
905  argc -= 3;
906  argv += 3;
907  flags = 0;
908 
909  while (argc >= 1) {
910  token = fr_str2int(filterKeywords, argv[0], -1);
911  switch (token) {
912  case FILTER_GENERIC_COMPNEQ:
913  if (flags & 0x01) return -1;
914  filter->compNeq = true;
915  flags |= 0x01;
916  break;
917  case FILTER_GENERIC_COMPEQ:
918  if (flags & 0x01) return -1;
919  filter->compNeq = false;
920  flags |= 0x01;
921  break;
922 
923  case FILTER_MORE:
924  if (flags & 0x02) return -1;
925  filter->more = htons( 1 );
926  flags |= 0x02;
927  break;
928 
929  default:
930  fr_strerror_printf("Invalid string \"%s\" in generic data filter",
931  argv[0]);
932  return -1;
933  }
934 
935  argc--;
936  argv++;
937  }
938 
939  return 0;
940 }
941 
942 
943 /** Filter binary
944  *
945  * This routine will call routines to parse entries from an ASCII format
946  * to a binary format recognized by the Ascend boxes.
947  *
948  * @param out Where to write parsed filter.
949  * @param value ascend filter text.
950  * @param len of value.
951  * @return
952  * - 0 on success.
953  * - -1 on failure.
954  */
955 int ascend_parse_filter(value_data_t *out, char const *value, size_t len)
956 {
957  int token, type;
958  int rcode;
959  int argc;
960  char *argv[32];
961  ascend_filter_t filter;
962  char *p;
963 
964  rcode = -1;
965 
966  /*
967  * Tokenize the input string in the VP.
968  *
969  * Once the filter is *completely* parsed, then we will
970  * over-write it with the final binary filter.
971  */
972  p = talloc_bstrndup(NULL, value, len);
973 
974  /*
975  * Rather than printing specific error messages, we create
976  * a general one here, which won't be used if the function
977  * returns OK.
978  */
979  fr_strerror_printf("Failed parsing \"%s\" as ascend filer", p);
980 
981  argc = fr_dict_str_to_argv(p, argv, 32);
982  if (argc < 3) {
983  talloc_free(p);
984  return -1;
985  }
986 
987  /*
988  * Decide which filter type it is: ip, ipx, or generic
989  */
990  type = fr_str2int(filterType, argv[0], -1);
991  memset(&filter, 0, sizeof(filter));
992 
993  /*
994  * Validate the filter type.
995  */
996  switch (type) {
997  case RAD_FILTER_GENERIC:
998  case RAD_FILTER_IP:
999  case RAD_FILTER_IPX:
1000  filter.type = type;
1001  break;
1002 
1003  default:
1004  fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]);
1005  talloc_free(p);
1006  return -1;
1007  }
1008 
1009  /*
1010  * Parse direction
1011  */
1012  token = fr_str2int(filterKeywords, argv[1], -1);
1013  switch (token) {
1014  case FILTER_IN:
1015  filter.direction = 1;
1016  break;
1017 
1018  case FILTER_OUT:
1019  filter.direction = 0;
1020  break;
1021 
1022  default:
1023  fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
1024  talloc_free(p);
1025  return -1;
1026  }
1027 
1028  /*
1029  * Parse action
1030  */
1031  token = fr_str2int(filterKeywords, argv[2], -1);
1032  switch (token) {
1033  case FILTER_FORWARD:
1034  filter.forward = 1;
1035  break;
1036 
1037  case FILTER_DROP:
1038  filter.forward = 0;
1039  break;
1040 
1041  default:
1042  fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]);
1043  talloc_free(p);
1044  return -1;
1045  }
1046 
1047 
1048  switch (type) {
1049  case RAD_FILTER_GENERIC:
1050  rcode = ascend_parse_generic(argc - 3, &argv[3], &filter.u.generic);
1051  break;
1052 
1053  case RAD_FILTER_IP:
1054  rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
1055  break;
1056 
1057  case RAD_FILTER_IPX:
1058  rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
1059  break;
1060  }
1061 
1062  /*
1063  * Touch the VP only if everything was OK.
1064  */
1065  if (rcode == 0) memcpy(out->filter, &filter, sizeof(filter));
1066  talloc_free(p);
1067 
1068  return rcode;
1069 }
1070 
1071 /*
1072  * Print an Ascend binary filter attribute to a string,
1073  * Grrr... Ascend makes the server do this work, instead
1074  * of doing it on the NAS.
1075  *
1076  * Note we don't bother checking 'len' after the snprintf's.
1077  * This function should ONLY be called with a large (~1k) buffer.
1078  */
1079 void print_abinary(char *out, size_t outlen, uint8_t const *data, size_t len, int8_t quote)
1080 {
1081  size_t i;
1082  char *p;
1083  ascend_filter_t const *filter;
1084 
1085  static char const *action[] = {"drop", "forward"};
1086  static char const *direction[] = {"out", "in"};
1087 
1088  p = out;
1089 
1090  /*
1091  * Just for paranoia: wrong size filters get printed as octets
1092  */
1093  if (len != sizeof(*filter)) {
1094  strcpy(p, "0x");
1095  p += 2;
1096  outlen -= 2;
1097  for (i = 0; i < len; i++) {
1098  snprintf(p, outlen, "%02x", data[i]);
1099  p += 2;
1100  outlen -= 2;
1101  }
1102  return;
1103  }
1104 
1105  if (quote > 0) {
1106  *(p++) = (char) quote;
1107  outlen -= 3; /* account for leading & trailing quotes */
1108  }
1109 
1110  filter = (ascend_filter_t const *) data;
1111  i = snprintf(p, outlen, "%s %s %s", fr_int2str(filterType, filter->type, "??"),
1112  direction[filter->direction & 0x01], action[filter->forward & 0x01]);
1113 
1114  p += i;
1115  outlen -= i;
1116 
1117  /*
1118  * Handle IP filters
1119  */
1120  if (filter->type == RAD_FILTER_IP) {
1121 
1122  if (filter->u.ip.srcip) {
1123  i = snprintf(p, outlen, " srcip %d.%d.%d.%d/%d",
1124  ((uint8_t const *) &filter->u.ip.srcip)[0],
1125  ((uint8_t const *) &filter->u.ip.srcip)[1],
1126  ((uint8_t const *) &filter->u.ip.srcip)[2],
1127  ((uint8_t const *) &filter->u.ip.srcip)[3],
1128  filter->u.ip.srcmask);
1129  p += i;
1130  outlen -= i;
1131  }
1132 
1133  if (filter->u.ip.dstip) {
1134  i = snprintf(p, outlen, " dstip %d.%d.%d.%d/%d",
1135  ((uint8_t const *) &filter->u.ip.dstip)[0],
1136  ((uint8_t const *) &filter->u.ip.dstip)[1],
1137  ((uint8_t const *) &filter->u.ip.dstip)[2],
1138  ((uint8_t const *) &filter->u.ip.dstip)[3],
1139  filter->u.ip.dstmask);
1140  p += i;
1141  outlen -= i;
1142  }
1143 
1144  i = snprintf(p, outlen, " %s", fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
1145  p += i;
1146  outlen -= i;
1147 
1148  if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
1149  i = snprintf(p, outlen, " srcport %s %d",
1150  fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
1151  ntohs(filter->u.ip.srcport));
1152  p += i;
1153  outlen -= i;
1154  }
1155 
1156  if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
1157  i = snprintf(p, outlen, " dstport %s %d",
1158  fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
1159  ntohs(filter->u.ip.dstport));
1160  p += i;
1161  outlen -= i;
1162  }
1163 
1164  if (filter->u.ip.established) {
1165  i = snprintf(p, outlen, " est");
1166  p += i;
1167  }
1168 
1169  /*
1170  * Handle IPX filters
1171  */
1172  } else if (filter->type == RAD_FILTER_IPX) {
1173  /* print for source */
1174  if (filter->u.ipx.src.net) {
1175  i = snprintf(p, outlen, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
1176  (unsigned int)ntohl(filter->u.ipx.src.net),
1177  filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
1178  filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
1179  filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
1180  p += i;
1181  outlen -= i;
1182 
1183  if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
1184  i = snprintf(p, outlen, " srcipxsock %s 0x%04x",
1185  fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
1186  ntohs(filter->u.ipx.src.socket));
1187  p += i;
1188  outlen -= i;
1189  }
1190  }
1191 
1192  /* same for destination */
1193  if (filter->u.ipx.dst.net) {
1194  i = snprintf(p, outlen, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
1195  (unsigned int)ntohl(filter->u.ipx.dst.net),
1196  filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
1197  filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
1198  filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
1199  p += i;
1200  outlen -= i;
1201 
1202  if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
1203  i = snprintf(p, outlen, " dstipxsock %s 0x%04x",
1204  fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
1205  ntohs(filter->u.ipx.dst.socket));
1206  p += i;
1207  }
1208  }
1209  } else if (filter->type == RAD_FILTER_GENERIC) {
1210  int count;
1211 
1212  i = snprintf(p, outlen, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
1213  p += i;
1214 
1215  /* show the mask */
1216  for (count = 0; count < ntohs(filter->u.generic.len); count++) {
1217  i = snprintf(p, outlen, "%02x", filter->u.generic.mask[count]);
1218  p += i;
1219  outlen -= i;
1220  }
1221 
1222  strcpy(p, " ");
1223  p++;
1224  outlen--;
1225 
1226  /* show the value */
1227  for (count = 0; count < ntohs(filter->u.generic.len); count++) {
1228  i = snprintf(p, outlen, "%02x", filter->u.generic.value[count]);
1229  p += i;
1230  outlen -= i;
1231  }
1232 
1233  i = snprintf(p, outlen, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
1234  p += i;
1235  outlen -= i;
1236 
1237  if (filter->u.generic.more != 0) {
1238  i = snprintf(p, outlen, " more");
1239  p += i;
1240  }
1241  }
1242 
1243  if (quote > 0) {
1244  *(p++) = (char) quote;
1245  }
1246  *p = '\0';
1247 }
1248 
1249 #endif
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
static char const * proto
Definition: radclient.c:63
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
static int comp(void const *a, void const *b)
Definition: rbmonkey.c:44
static uint32_t mask
Definition: rbmonkey.c:75
size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
Convert hex strings to binary data.
Definition: misc.c:220
uint8_t data[]
Definition: eap_pwd.h:625
char * talloc_bstrndup(void const *t, char const *in, size_t inlen)
Binary safe strndup function.
Definition: missing.c:632
uint8_t token[4]
Definition: eap_pwd.h:625
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
#define RCSID(id)
Definition: build.h:135
int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
Definition: dict.c:1304