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