1 /* $Id: upnppermissions.c,v 1.19 2014/03/13 10:11:24 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  */
24 int
read_permission_line(struct upnpperm * perm,char * p)25 read_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
185 void
write_permlist(int fd,const struct upnpperm * permary,int nperms)186 write_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 */
212 static int
match_permission(const struct upnpperm * perm,u_short eport,struct in_addr address,u_short iport)213 match_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 */
230 static int
231 match_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 
243 int
check_upnp_rule_against_permissions(const struct upnpperm * permary,int n_perms,u_short eport,struct in_addr address,u_short iport)244 check_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