1/* $Id: upnppermissions.c,v 1.18 2014/03/07 10:43:29 nanard Exp $ */
2/* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2014 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
7
8#include <ctype.h>
9#include <string.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <syslog.h>
13#include <netinet/in.h>
14#include <arpa/inet.h>
15#include <unistd.h>
16#include "config.h"
17#include "upnppermissions.h"
18
19/* read_permission_line()
20 * parse the a permission line which format is :
21 * (deny|allow) [0-9]+(-[0-9]+) ip/mask [0-9]+(-[0-9]+)
22 * ip/mask is either 192.168.1.1/24 or 192.168.1.1/255.255.255.0
23 */
24int
25read_permission_line(struct upnpperm * perm,
26                     char * p)
27{
28	char * q;
29	int n_bits;
30	int i;
31
32	/* first token: (allow|deny) */
33	while(isspace(*p))
34		p++;
35	if(0 == memcmp(p, "allow", 5))
36	{
37		perm->type = UPNPPERM_ALLOW;
38		p += 5;
39	}
40	else if(0 == memcmp(p, "deny", 4))
41	{
42		perm->type = UPNPPERM_DENY;
43		p += 4;
44	}
45	else
46	{
47		return -1;
48	}
49	while(isspace(*p))
50		p++;
51
52	/* second token: eport or eport_min-eport_max */
53	if(!isdigit(*p))
54		return -1;
55	for(q = p; isdigit(*q); q++);
56	if(*q=='-')
57	{
58		*q = '\0';
59		i = atoi(p);
60		if(i > 65535)
61			return -1;
62		perm->eport_min = (u_short)i;
63		q++;
64		p = q;
65		while(isdigit(*q))
66			q++;
67		*q = '\0';
68		i = atoi(p);
69		if(i > 65535)
70			return -1;
71		perm->eport_max = (u_short)i;
72		if(perm->eport_min > perm->eport_max)
73			return -1;
74	}
75	else if(isspace(*q))
76	{
77		*q = '\0';
78		i = atoi(p);
79		if(i > 65535)
80			return -1;
81		perm->eport_min = perm->eport_max = (u_short)i;
82	}
83	else
84	{
85		return -1;
86	}
87	p = q + 1;
88	while(isspace(*p))
89		p++;
90
91	/* third token:  ip/mask */
92	if(!isdigit(*p))
93		return -1;
94	for(q = p; isdigit(*q) || (*q == '.'); q++);
95	if(*q=='/')
96	{
97		*q = '\0';
98		if(!inet_aton(p, &perm->address))
99			return -1;
100		q++;
101		p = q;
102		while(isdigit(*q))
103			q++;
104		if(*q == '.')
105		{
106			while(*q == '.' || isdigit(*q))
107				q++;
108			if(!isspace(*q))
109				return -1;
110			*q = '\0';
111			if(!inet_aton(p, &perm->mask))
112				return -1;
113		}
114		else if(!isspace(*q))
115			return -1;
116		else
117		{
118			*q = '\0';
119			n_bits = atoi(p);
120			if(n_bits > 32)
121				return -1;
122			perm->mask.s_addr = htonl(n_bits ? (0xffffffffu << (32 - n_bits)) : 0);
123		}
124	}
125	else if(isspace(*q))
126	{
127		*q = '\0';
128		if(!inet_aton(p, &perm->address))
129			return -1;
130		perm->mask.s_addr = 0xffffffffu;
131	}
132	else
133	{
134		return -1;
135	}
136	p = q + 1;
137
138	/* fourth token: iport or iport_min-iport_max */
139	while(isspace(*p))
140		p++;
141	if(!isdigit(*p))
142		return -1;
143	for(q = p; isdigit(*q); q++);
144	if(*q=='-')
145	{
146		*q = '\0';
147		i = atoi(p);
148		if(i > 65535)
149			return -1;
150		perm->iport_min = (u_short)i;
151		q++;
152		p = q;
153		while(isdigit(*q))
154			q++;
155		*q = '\0';
156		i = atoi(p);
157		if(i > 65535)
158			return -1;
159		perm->iport_max = (u_short)i;
160		if(perm->iport_min > perm->iport_max)
161			return -1;
162	}
163	else if(isspace(*q) || *q == '\0')
164	{
165		*q = '\0';
166		i = atoi(p);
167		if(i > 65535)
168			return -1;
169		perm->iport_min = perm->iport_max = (u_short)i;
170	}
171	else
172	{
173		return -1;
174	}
175#ifdef DEBUG
176	printf("perm rule added : %s %hu-%hu %08x/%08x %hu-%hu\n",
177	       (perm->type==UPNPPERM_ALLOW)?"allow":"deny",
178	       perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr),
179	       ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max);
180#endif
181	return 0;
182}
183
184#ifdef USE_MINIUPNPDCTL
185void
186write_permlist(int fd, const struct upnpperm * permary,
187               int nperms)
188{
189	int l;
190	const struct upnpperm * perm;
191	int i;
192	char buf[128];
193	write(fd, "Permissions :\n", 14);
194	for(i = 0; i<nperms; i++)
195	{
196		perm = permary + i;
197		l = snprintf(buf, sizeof(buf), "%02d %s %hu-%hu %08x/%08x %hu-%hu\n",
198	       i,
199    	   (perm->type==UPNPPERM_ALLOW)?"allow":"deny",
200	       perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr),
201	       ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max);
202		if(l<0)
203			return;
204		write(fd, buf, l);
205	}
206}
207#endif
208
209/* match_permission()
210 * returns: 1 if eport, address, iport matches the permission rule
211 *          0 if no match */
212static int
213match_permission(const struct upnpperm * perm,
214                 u_short eport, struct in_addr address, u_short iport)
215{
216	if( (eport < perm->eport_min) || (perm->eport_max < eport))
217		return 0;
218	if( (iport < perm->iport_min) || (perm->iport_max < iport))
219		return 0;
220	if( (address.s_addr & perm->mask.s_addr)
221	   != (perm->address.s_addr & perm->mask.s_addr) )
222		return 0;
223	return 1;
224}
225
226#if 0
227/* match_permission_internal()
228 * returns: 1 if address, iport matches the permission rule
229 *          0 if no match */
230static int
231match_permission_internal(const struct upnpperm * perm,
232                          struct in_addr address, u_short iport)
233{
234	if( (iport < perm->iport_min) || (perm->iport_max < iport))
235		return 0;
236	if( (address.s_addr & perm->mask.s_addr)
237	   != (perm->address.s_addr & perm->mask.s_addr) )
238		return 0;
239	return 1;
240}
241#endif
242
243int
244check_upnp_rule_against_permissions(const struct upnpperm * permary,
245                                    int n_perms,
246                                    u_short eport, struct in_addr address,
247                                    u_short iport)
248{
249	int i;
250	for(i=0; i<n_perms; i++)
251	{
252		if(match_permission(permary + i, eport, address, iport))
253		{
254			syslog(LOG_DEBUG,
255			       "UPnP permission rule %d matched : port mapping %s",
256			       i, (permary[i].type == UPNPPERM_ALLOW)?"accepted":"rejected"
257			       );
258			return (permary[i].type == UPNPPERM_ALLOW);
259		}
260	}
261	syslog(LOG_DEBUG, "no permission rule matched : accept by default (n_perms=%d)", n_perms);
262	return 1;	/* Default : accept */
263}
264
265