1 /*
2 * filters.c Routines to parse Ascend's filter attributes.
3 *
4 * Version: $Id: 4868cd385d9faaff1495cf79d2bfd75866884a91 $
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: 4868cd385d9faaff1495cf79d2bfd75866884a91 $")
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 */
ascend_parse_ipx_net(int argc,char ** argv,ascend_ipx_net_t * net,uint8_t * comp)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 */
ascend_parse_ipx(int argc,char ** argv,ascend_ipx_filter_t * filter)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 error, or the number of bits in the netmask, otherwise.
556 */
ascend_parse_ipaddr(uint32_t * ipaddr,char * str)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 error, or the comparator.
653 */
ascend_parse_port(uint16_t * port,char * compare,char * str)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 */
ascend_parse_ip(int argc,char ** argv,ascend_ip_filter_t * filter)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 */
ascend_parse_generic(int argc,char ** argv,ascend_generic_filter_t * filter)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 -1 for error or 0.
952 */
ascend_parse_filter(value_data_t * out,char const * value,size_t len)953 int ascend_parse_filter(value_data_t *out, char const *value, size_t len)
954 {
955 int token, type;
956 int rcode;
957 int argc;
958 char *argv[32];
959 ascend_filter_t filter;
960 char *p;
961
962 rcode = -1;
963
964 /*
965 * Tokenize the input string in the VP.
966 *
967 * Once the filter is *completely* parsed, then we will
968 * over-write it with the final binary filter.
969 */
970 p = talloc_bstrndup(NULL, value, len);
971
972 /*
973 * Rather than printing specific error messages, we create
974 * a general one here, which won't be used if the function
975 * returns OK.
976 */
977 fr_strerror_printf("Failed parsing \"%s\" as ascend filer", p);
978
979 argc = str2argv(p, argv, 32);
980 if (argc < 3) {
981 talloc_free(p);
982 return -1;
983 }
984
985 /*
986 * Decide which filter type it is: ip, ipx, or generic
987 */
988 type = fr_str2int(filterType, argv[0], -1);
989 memset(&filter, 0, sizeof(filter));
990
991 /*
992 * Validate the filter type.
993 */
994 switch (type) {
995 case RAD_FILTER_GENERIC:
996 case RAD_FILTER_IP:
997 case RAD_FILTER_IPX:
998 filter.type = type;
999 break;
1000
1001 default:
1002 fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]);
1003 talloc_free(p);
1004 return -1;
1005 }
1006
1007 /*
1008 * Parse direction
1009 */
1010 token = fr_str2int(filterKeywords, argv[1], -1);
1011 switch (token) {
1012 case FILTER_IN:
1013 filter.direction = 1;
1014 break;
1015
1016 case FILTER_OUT:
1017 filter.direction = 0;
1018 break;
1019
1020 default:
1021 fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
1022 talloc_free(p);
1023 return -1;
1024 }
1025
1026 /*
1027 * Parse action
1028 */
1029 token = fr_str2int(filterKeywords, argv[2], -1);
1030 switch (token) {
1031 case FILTER_FORWARD:
1032 filter.forward = 1;
1033 break;
1034
1035 case FILTER_DROP:
1036 filter.forward = 0;
1037 break;
1038
1039 default:
1040 fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]);
1041 talloc_free(p);
1042 return -1;
1043 }
1044
1045
1046 switch (type) {
1047 case RAD_FILTER_GENERIC:
1048 rcode = ascend_parse_generic(argc - 3, &argv[3], &filter.u.generic);
1049 break;
1050
1051 case RAD_FILTER_IP:
1052 rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
1053 break;
1054
1055 case RAD_FILTER_IPX:
1056 rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
1057 break;
1058 }
1059
1060 /*
1061 * Touch the VP only if everything was OK.
1062 */
1063 if (rcode == 0) memcpy(out->filter, &filter, sizeof(filter));
1064 talloc_free(p);
1065
1066 return rcode;
1067 }
1068
1069 /*
1070 * Print an Ascend binary filter attribute to a string,
1071 * Grrr... Ascend makes the server do this work, instead
1072 * of doing it on the NAS.
1073 *
1074 * Note we don't bother checking 'len' after the snprintf's.
1075 * This function should ONLY be called with a large (~1k) buffer.
1076 */
print_abinary(char * out,size_t outlen,uint8_t const * data,size_t len,int8_t quote)1077 void print_abinary(char *out, size_t outlen, uint8_t const *data, size_t len, int8_t quote)
1078 {
1079 size_t i;
1080 char *p;
1081 ascend_filter_t const *filter;
1082
1083 static char const *action[] = {"drop", "forward"};
1084 static char const *direction[] = {"out", "in"};
1085
1086 p = out;
1087
1088 /*
1089 * Just for paranoia: wrong size filters get printed as octets
1090 */
1091 if (len != sizeof(*filter)) {
1092 strcpy(p, "0x");
1093 p += 2;
1094 outlen -= 2;
1095 for (i = 0; i < len; i++) {
1096 snprintf(p, outlen, "%02x", data[i]);
1097 p += 2;
1098 outlen -= 2;
1099 }
1100 return;
1101 }
1102
1103 if (quote > 0) {
1104 *(p++) = (char) quote;
1105 outlen -= 3; /* account for leading & trailing quotes */
1106 }
1107
1108 filter = (ascend_filter_t const *) data;
1109 i = snprintf(p, outlen, "%s %s %s", fr_int2str(filterType, filter->type, "??"),
1110 direction[filter->direction & 0x01], action[filter->forward & 0x01]);
1111
1112 p += i;
1113 outlen -= i;
1114
1115 /*
1116 * Handle IP filters
1117 */
1118 if (filter->type == RAD_FILTER_IP) {
1119
1120 if (filter->u.ip.srcip) {
1121 i = snprintf(p, outlen, " srcip %d.%d.%d.%d/%d",
1122 ((uint8_t const *) &filter->u.ip.srcip)[0],
1123 ((uint8_t const *) &filter->u.ip.srcip)[1],
1124 ((uint8_t const *) &filter->u.ip.srcip)[2],
1125 ((uint8_t const *) &filter->u.ip.srcip)[3],
1126 filter->u.ip.srcmask);
1127 p += i;
1128 outlen -= i;
1129 }
1130
1131 if (filter->u.ip.dstip) {
1132 i = snprintf(p, outlen, " dstip %d.%d.%d.%d/%d",
1133 ((uint8_t const *) &filter->u.ip.dstip)[0],
1134 ((uint8_t const *) &filter->u.ip.dstip)[1],
1135 ((uint8_t const *) &filter->u.ip.dstip)[2],
1136 ((uint8_t const *) &filter->u.ip.dstip)[3],
1137 filter->u.ip.dstmask);
1138 p += i;
1139 outlen -= i;
1140 }
1141
1142 i = snprintf(p, outlen, " %s", fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
1143 p += i;
1144 outlen -= i;
1145
1146 if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
1147 i = snprintf(p, outlen, " srcport %s %d",
1148 fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
1149 ntohs(filter->u.ip.srcport));
1150 p += i;
1151 outlen -= i;
1152 }
1153
1154 if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
1155 i = snprintf(p, outlen, " dstport %s %d",
1156 fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
1157 ntohs(filter->u.ip.dstport));
1158 p += i;
1159 outlen -= i;
1160 }
1161
1162 if (filter->u.ip.established) {
1163 i = snprintf(p, outlen, " est");
1164 p += i;
1165 }
1166
1167 /*
1168 * Handle IPX filters
1169 */
1170 } else if (filter->type == RAD_FILTER_IPX) {
1171 /* print for source */
1172 if (filter->u.ipx.src.net) {
1173 i = snprintf(p, outlen, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
1174 (unsigned int)ntohl(filter->u.ipx.src.net),
1175 filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
1176 filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
1177 filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
1178 p += i;
1179 outlen -= i;
1180
1181 if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
1182 i = snprintf(p, outlen, " srcipxsock %s 0x%04x",
1183 fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
1184 ntohs(filter->u.ipx.src.socket));
1185 p += i;
1186 outlen -= i;
1187 }
1188 }
1189
1190 /* same for destination */
1191 if (filter->u.ipx.dst.net) {
1192 i = snprintf(p, outlen, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
1193 (unsigned int)ntohl(filter->u.ipx.dst.net),
1194 filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
1195 filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
1196 filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
1197 p += i;
1198 outlen -= i;
1199
1200 if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
1201 i = snprintf(p, outlen, " dstipxsock %s 0x%04x",
1202 fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
1203 ntohs(filter->u.ipx.dst.socket));
1204 p += i;
1205 }
1206 }
1207 } else if (filter->type == RAD_FILTER_GENERIC) {
1208 int count;
1209
1210 i = snprintf(p, outlen, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
1211 p += i;
1212
1213 /* show the mask */
1214 for (count = 0; count < ntohs(filter->u.generic.len); count++) {
1215 i = snprintf(p, outlen, "%02x", filter->u.generic.mask[count]);
1216 p += i;
1217 outlen -= i;
1218 }
1219
1220 strcpy(p, " ");
1221 p++;
1222 outlen--;
1223
1224 /* show the value */
1225 for (count = 0; count < ntohs(filter->u.generic.len); count++) {
1226 i = snprintf(p, outlen, "%02x", filter->u.generic.value[count]);
1227 p += i;
1228 outlen -= i;
1229 }
1230
1231 i = snprintf(p, outlen, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
1232 p += i;
1233 outlen -= i;
1234
1235 if (filter->u.generic.more != 0) {
1236 i = snprintf(p, outlen, " more");
1237 p += i;
1238 }
1239 }
1240
1241 if (quote > 0) {
1242 *(p++) = (char) quote;
1243 }
1244 *p = '\0';
1245 }
1246
1247 #endif
1248