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